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 }
