Netty之基于Http协议通信(HelloWorld&文件服务器)

1、简单介绍HTTP协议:

        Http协议即超文本传输协议,是建立在TCP传输协议之上的应用层协议,他目前主流是针对WEB开发,HTTP协议应用非常广泛,因此掌握HTTP协议的开发非常之重要。我们主要讲解Netty如何基于HTTP协议进行开发,那么使用Netty的HTTP协议也是异步非阻塞的。

2、HTTP协议的特点:

        简单:客户端请求服务器时主需要指定URL和携带必要的参数即可。

        灵活:HTTP协议允许传输任意类型的数据类型,传输内容由HTTP消息头中的Content-Type加以标记。

        无连接:无连接的含义是限制每次连接只处理一个请求。服务器处理客户的请求,并受到客户端的应答后,就断开连接。采用这种方式可以节省传输时间。

        无状态:HTTP协议是无状态的,无状态指的是协议对事务处理没有记忆能力。这意味着如果后续处理需要之前的信息,则它必须重新获取。也从侧面体现了HTTP的设计是为了使网络传输更加的轻量级、敏捷、负载较轻。

3、HTTP协议的组成部分:

     请求行:用来说明请求类型,要访问的资源以及所使用的HTTP版本。

     请求头:紧接着请求行(第一行)之后的部分,用来说明服务器要使用的附加信息。

     空行:请求头部后面的空行是必须的。即使第四部分的请求正文是空的也必须要有空行。

      请求正文(实体内容):可以添加任意的其他数据。

4、HTTP协议的请求方式:

       GET:获取Reqeust-URI所标识的资源

       POST:在Request-URI所标识的资源附加新的提交数据

       HEAD:请求获取由Request-URI所标识的资源的响应消息头

       PUT:请求服务器存储一个资源,并用Request_URI作为一个标识

       DELETE:请求服务器删除Request-URI所标识的资源

        TRACE:请求服务器回送收到的请求信息,主要是测试和诊断使用(@trace)

        CONNECT:保留将来使用

        OPTIONS:请求查询服务器的性能

5、HTTP协议的响应消息

      响应消息由三部分组成:状态行、消息头、响应正文。

      响应状态的种类:

             1xx:提示消息。表示请求已经接收继续处理。

             2xx:成功。表示请求已经接收成功。

             3xx:重定向。要完成的请求必须进行更一步的操作。

             4xx:客户端错误。可能是请求语法错误或者请求无法实现。

             5xx:服务端错误。服务器未能处理请求(可能内部出现异常)。

      常用相应状态码:

             200 OK:成功。

             400 Bad Request :错误的请求语法,不能被服务器理解。

             401 Unauthorized:请求未经授权。

             403 Forbidden:服务器收到请求,但请求被服务器拒绝。

             404 Not Found:请求资源不存在。

             405 Method Not Allowed:请求方式不被允许,如只支持get请求,但客户端使用了post请求。

             500 Inernal Server Error:服务器发生不可预测的错误。

             503 Server Unavailable:服务器当前不能处理客户端请求,一段时间后可能恢复正常

Netty关于HTTP的例子都是从官网上面拿的。首先是Hello World,然后是文件服务器(上传和下载)。

服务器Server:

package com.demo.testNetty.http;

import java.security.cert.CertificateException;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.util.SelfSignedCertificate;

public class HttpHelloWorldServer {
	//这个是拿来判断有没有安全协议的。
	static final boolean SSL = System.getProperty("ssl") != null;
    static final int PORT = Integer.parseInt(System.getProperty("port", SSL? "8443" : "8080"));
    
	public static void main(String[] args) throws Exception {
		
		// 配置 SSL.
        final SslContext sslCtx;
        if (SSL) {
            SelfSignedCertificate ssc = new SelfSignedCertificate();
            sslCtx = SslContext.newServerContext(ssc.certificate(), ssc.privateKey());
        } else {
            sslCtx = null;
        }
        
		//配置服务器
		EventLoopGroup bossGroup = new NioEventLoopGroup();
		EventLoopGroup workGroup = new NioEventLoopGroup();
		try{
			ServerBootstrap sb = new ServerBootstrap();
			
			sb.group(bossGroup, workGroup)
			.channel(NioServerSocketChannel.class)
			.option(ChannelOption.SO_BACKLOG, 1024)
			.handler(new LoggingHandler(LogLevel.INFO))
			//需要将SSL带到初始化那
			.childHandler(new HttpHelloWorldServerInitializer(sslCtx));
			
			//绑定端口PORT
			ChannelFuture ch = sb.bind(PORT).sync();
			System.err.println("Open your web browser and navigate to " +
	                (SSL? "https" : "http") + "://127.0.0.1:" + PORT + '/');
			ch.channel().closeFuture().sync();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}finally{
			bossGroup.shutdownGracefully();
			workGroup.shutdownGracefully();
		}
		
	}
}

服务器Initializer:

package com.demo.testNetty.http;

import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.ssl.SslContext;

public class HttpHelloWorldServerInitializer extends ChannelInitializer<SocketChannel>{

	private final SslContext sslCtx;
	  
    public HttpHelloWorldServerInitializer(SslContext sslCtx) {
         this.sslCtx = sslCtx;
      }
    
	@Override
	protected void initChannel(SocketChannel ch) throws Exception {
		ChannelPipeline p = ch.pipeline();
		if(sslCtx != null){  //如果安全协议不为空,则要添加多一个handler
			p.addLast(sslCtx.newHandler(ch.alloc()));
		}
		p.addLast(new HttpServerCodec()); //http通信的必须.通信都使用HttpRequest和HttpResponse(当然了,是Netty自带的)
		p.addLast(new HttpHelloWorldServerHandler()); //自己的业务处理Handler
		
	}

}

服务器Handler:

package com.demo.testNetty.http;

import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpHeaderUtil;
import io.netty.handler.codec.http.HttpHeaderValues;
import io.netty.handler.codec.http.HttpRequest;
import static io.netty.handler.codec.http.HttpHeaderNames.*;
import static io.netty.handler.codec.http.HttpResponseStatus.*;
import static io.netty.handler.codec.http.HttpVersion.*;
public class HttpHelloWorldServerHandler extends ChannelHandlerAdapter{

	private static final byte[] CONTENT = { 'M', 'O', 'S', 'I', ' ', 'M', 'O', 'S', 'I', };
	
	@Override
    public void channelReadComplete(ChannelHandlerContext ctx) {
        ctx.flush();  //加上这个是最稳的,万一真的忘记flush 哈哈
    }
	
	@Override
	public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
		if(msg instanceof HttpRequest){
			HttpRequest request  = (HttpRequest) msg;
			if (HttpHeaderUtil.is100ContinueExpected(request)) {
                ctx.write(new DefaultFullHttpResponse(HTTP_1_1, CONTINUE));
            }
            boolean keepAlive = HttpHeaderUtil.isKeepAlive(request);
            //构造响应 设置消息头
            FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, OK, Unpooled.wrappedBuffer(CONTENT));
            response.headers().set(CONTENT_TYPE, "text/plain");
            response.headers().setInt(CONTENT_LENGTH, response.content().readableBytes());

            if (!keepAlive) {  //如果是短连接,发送完就关闭通道
                ctx.write(response).addListener(ChannelFutureListener.CLOSE);
            } else { //如果是长连接,消息头设置,连接保持活跃
                response.headers().set(CONNECTION, HttpHeaderValues.KEEP_ALIVE); 
                ctx.write(response);
            }
		}
	}
	
	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
		cause.printStackTrace();
	}
	
}	

客户端呢,就是我们的浏览器咯。

执行服务器:

[11:43:05] nioEventLoopGroup-0-0 INFO  [] [] [io.netty.handler.logging.LoggingHandler] - [id: 0x1ebcdcc2] REGISTERED
Open your web browser and navigate to http://127.0.0.1:8080/
[11:43:05] nioEventLoopGroup-0-0 INFO  [] [] [io.netty.handler.logging.LoggingHandler] - [id: 0x1ebcdcc2] BIND: 0.0.0.0/0.0.0.0:8080
[11:43:05] nioEventLoopGroup-0-0 INFO  [] [] [io.netty.handler.logging.LoggingHandler] - [id: 0x1ebcdcc2, /0:0:0:0:0:0:0:0:8080] ACTIVE

我们复制上面的URL再客户端访问以下。

文件的上传和下载就不在这里弄了。

给个代码的连接:http://note.youdao.com/noteshare?id=487954c7b6254e905da5146fcfbc02f7

猜你喜欢

转载自blog.csdn.net/Howinfun/article/details/81428595