Netty入门(八)构建Netty HTTP/HTTPS应用
HTTP/HTTPS 是最常见的一种协议,这节主要是看一下 Netty 提供的 ChannelHaandler。
一、HTTP Decoder,Encoder 和 Codec
HTTP 是请求-响应模式,客户端发送一个 HTTP 请求,服务就响应此请求。
HttpRequest 包格式如下:
- 包头
- 数据部分,后续可以有多个 HttpContent 部分
- 包尾,标记 request 包结束,同时可能包含头的尾部信息
- 完整的 HTTP request
HttpResponce 包格式如下:
- 包头
- 数据部分,后续可以有多个 HttpContent 部分
- 包尾,标记 responce 包结束,同时可能包含头的尾部信息
- 完整的 HTTP responce
下面是 Netty 提供的解码器和编码器用来处理上述的包信息:
所以,如果我们想要在应用程序中支持 HTTP,只需要添加正确的 ChannelHandler 到 ChannelPipeline 中即可:
1 public class HttpPipelineInitializer extends ChannelInitializer<Channel> { 2 private final boolean client; 3 4 public HttpPipelineInitializer(boolean client) { 5 this.client = client; 6 } 7 8 @Override 9 protected void initChannel(Channel ch) throws Exception { 10 ChannelPipeline pipeline = ch.pipeline(); 11 if(client) { 12 // 客户端需要解码服务器响应,编码客户端请求 13 pipeline.addLast("decoder", new HttpResponseDecoder()); 14 pipeline.addLast("encoder", new HttpRequestEncoder()); 15 } else { 16 // 服务端需要解码客户端请求,编码服务端响应 17 pipeline.addLast("decoder", new HttpRequestDecoder()); 18 pipeline.addLast("encoder", new HttpResponseEncoder()); 19 } 20 } 21 22 }
二、HTTP 消息聚合
由于 HTTP 请求和响应消息部分可以由许多块组成,我们需要聚合它们形成完整的消息。Netty 提供了一个聚合器。如下为简单实现:
1 /** 2 * HTTP 消息聚合 3 * HttpObjectAggregator 4 */ 5 public class HttpAggregatorInitializer extends ChannelInitializer<Channel> { 6 private final boolean client; 7 8 public HttpAggregatorInitializer(boolean client) { 9 this.client = client; 10 } 11 12 @Override 13 protected void initChannel(Channel ch) throws Exception { 14 ChannelPipeline pipeline = ch.pipeline(); 15 if(client) { 16 // 客户端 17 pipeline.addLast("codec", new HttpClientCodec()); 18 } else { 19 // 服务器 20 pipeline.addLast("codec", new HttpServerCodec()); 21 } 22 // HTTP聚合,设置最大消息值为512KB 23 pipeline.addLast("aggegator", new HttpObjectAggregator(512 * 1024)); 24 } 25 26 }
三、HTTP 压缩
使用 HTTP 时建议压缩数据以减少传输流量,Netty 支持 “gzip”和“deflate”。简单实现如下:
1 /** 2 * HTTP 压缩 3 * HttpContentDecompressor 用于客户端解压缩 4 * HttpContentCompressor 用于服务器压缩 5 */ 6 public class HttpCompressorInitializer extends ChannelInitializer<Channel> { 7 private final boolean client; 8 9 public HttpCompressorInitializer(boolean client) { 10 this.client = client; 11 } 12 13 @Override 14 protected void initChannel(Channel ch) throws Exception { 15 ChannelPipeline pipeline = ch.pipeline(); 16 if(client) { 17 // 客户端 18 pipeline.addLast("codec", new HttpClientCodec()); 19 // 解压缩,用于处理来自服务器的压缩内容 20 pipeline.addLast("decompressor", new HttpContentDecompressor()); 21 } else { 22 // 服务端 23 pipeline.addLast("codec", new HttpServerCodec()); 24 // 压缩,将要发送的消息压缩后再发出 25 pipeline.addLast("compressor", new HttpContentCompressor()); 26 } 27 } 28 29 }
四、使用 HTTPS
启动 HTTPS(比 HTTP 安全),只需添加 SslHandler。简单实现如下:
1 /** 2 * HTTPS 3 */ 4 public class HttpsCodecInitializer extends ChannelInitializer<Channel> { 5 private final SslContext context; 6 private final boolean client; 7 8 public HttpsCodecInitializer(SslContext context, boolean client) { 9 this.context = context; 10 this.client = client; 11 } 12 13 @Override 14 protected void initChannel(Channel ch) throws Exception { 15 ChannelPipeline pipeline = ch.pipeline(); 16 SSLEngine engine = context.newEngine(ch.alloc()); 17 18 // 添加SslHandler以启用HTTPS 19 pipeline.addFirst("ssl", new SslHandler(engine)); 20 if(client) { 21 // 客户端 22 pipeline.addLast("codec", new HttpClientCodec()); 23 } else { 24 // 服务端 25 pipeline.addLast("codec", new HttpServerCodec()); 26 } 27 } 28 29 }
五、WebSocket
WebSocket 允许数据双向传输,而不需要请求-响应模式。当我们需要服务器主动向客户端发送消息,比如实时系统,WebSocket 就是一个不错的选择。下面是一个通用的 WebSocket 协议:
- Client(HTTP)与 Server 通讯
- Server(HTTP)与 Client 通讯
- Client 通过 HTTP(s) 来进行 WebSocket 握手,并等待确认
- 连接协议升级至 WebSocket
应用程序支持 WebSocket 只需要添加适当的客户端或服务器端 WebSocket ChannelHandler 到管道。这个类将处理 WebSocket 定义的信息类型,称为“帧”。帧类型可分为数据帧和控制帧,如下:
简单实现如下:
1 /** 2 * WebSocket 3 * WebSocketServerProtocolHandler 处理其他类型帧 4 * TextFrameHandler BinaryFrameHandler ContinuationFrameHandler 5 */ 6 public class WebSocketServerInitializer extends ChannelInitializer<Channel> { 7 8 @Override 9 protected void initChannel(Channel ch) throws Exception { 10 ch.pipeline().addLast( 11 new HttpServerCodec(), 12 new HttpObjectAggregator(65536), // HTTP 聚合 13 // 处理除指定Frame之外的其他类型帧,比如Ping,Pong,Close等 14 new WebSocketServerProtocolHandler("/websocket"), 15 new TextFrameHandler(), 16 new BinaryFrameHandler(), 17 new ContinuationFrameHandler()); 18 } 19 20 // Text Frame 21 public static final class TextFrameHandler 22 extends SimpleChannelInboundHandler<TextWebSocketFrame> { 23 @Override 24 protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception { 25 // TODO Handle Text Frame 26 } 27 } 28 29 // Binary Frame 30 public static final class BinaryFrameHandler 31 extends SimpleChannelInboundHandler<BinaryWebSocketFrame> { 32 @Override 33 protected void channelRead0(ChannelHandlerContext ctx, BinaryWebSocketFrame msg) throws Exception { 34 // TODO Handle Text Frame 35 } 36 } 37 38 // Continuation Frame 39 public static final class ContinuationFrameHandler 40 extends SimpleChannelInboundHandler<ContinuationWebSocketFrame> { 41 @Override 42 protected void channelRead0(ChannelHandlerContext ctx, ContinuationWebSocketFrame msg) throws Exception { 43 // TODO Handle Text Frame 44 } 45 } 46 }