HTTP/HTTPS 是最常見的一種協議,在智能手機里廣泛應用。雖然每家公司都有一個主頁,您可以通過HTTP或HTTPS訪問,這不是它唯一的使用。許多組織通過 HTTP(S) 公開 WebService API ,旨在用于緩解獨立的平臺帶來的弊端 。 讓我們看一下 Netty 提供的 ChannelHandler,是如何允許您使用 HTTP 和 HTTPS 而無需編寫自己的編解碼器。
### [](https://github.com/waylau/essential-netty-in-action/blob/master/CORE%20FUNCTIONS/Building%20Netty%20HTTPHTTPS%20applications.md#http-decoder-encoder-和-codec)HTTP Decoder, Encoder 和 Codec
HTTP 是請求-響應模式,客戶端發送一個 HTTP 請求,服務就響應此請求。Netty 提供了簡單的編碼、解碼器來簡化基于這個協議的開發工作。圖8.2和圖8.3顯示 HTTP 請求和響應的方法是如何生產和消費的
[](https://github.com/waylau/essential-netty-in-action/blob/master/images/Figure%208.2%20HTTP%20request%20component%20parts.jpg)
1. HTTP Request 第一部分是包含的頭信息
2. HttpContent 里面包含的是數據,可以后續有多個 HttpContent 部分
3. LastHttpContent 標記是 HTTP request 的結束,同時可能包含頭的尾部信息
4. 完整的 HTTP request
Figure 8.2 HTTP request component parts
[](https://github.com/waylau/essential-netty-in-action/blob/master/images/Figure%208.3%20HTTP%20response%20component%20parts.jpg)
1. HTTP response 第一部分是包含的頭信息
2. HttpContent 里面包含的是數據,可以后續有多個 HttpContent 部分
3. LastHttpContent 標記是 HTTP response 的結束,同時可能包含頭的尾部信息
4. 完整的 HTTP response
Figure 8.3 HTTP response component parts
如圖8.2和8.3所示的 HTTP 請求/響應可能包含不止一個數據部分,它總是終止于 LastHttpContent 部分。FullHttpRequest 和FullHttpResponse 消息是特殊子類型,分別表示一個完整的請求和響應。所有類型的 HTTP 消息(FullHttpRequest ,LastHttpContent 以及那些如清單8.2所示)實現 HttpObject 接口。
表8.2概述 HTTP 解碼器和編碼器的處理和生產這些消息。
Table 8.2 HTTP decoder and encoder
| 名稱 | 描述 |
| --- | --- |
| HttpRequestEncoder | Encodes HttpRequest , HttpContent and LastHttpContent messages to bytes. |
| HttpResponseEncoder | Encodes HttpResponse, HttpContent and LastHttpContent messages to bytes. |
| HttpRequestDecoder | Decodes bytes into HttpRequest, HttpContent and LastHttpContent messages. |
| HttpResponseDecoder | Decodes bytes into HttpResponse, HttpContent and LastHttpContent messages. |
清單8.2所示的是將支持 HTTP 添加到您的應用程序是多么簡單。僅僅添加正確的 ChannelHandler 到 ChannelPipeline 中
Listing 8.2 Add support for HTTP
~~~
public class HttpPipelineInitializer extends ChannelInitializer<Channel> {
private final boolean client;
public HttpPipelineInitializer(boolean client) {
this.client = client;
}
@Override
protected void initChannel(Channel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
if (client) {
pipeline.addLast("decoder", new HttpResponseDecoder()); //1
pipeline.addLast("encoder", new HttpRequestEncoder()); //2
} else {
pipeline.addLast("decoder", new HttpRequestDecoder()); //3
pipeline.addLast("encoder", new HttpResponseEncoder()); //4
}
}
}
~~~
1. client: 添加 HttpResponseDecoder 用于處理來自 server 響應
2. client: 添加 HttpRequestEncoder 用于發送請求到 server
3. server: 添加 HttpRequestDecoder 用于接收來自 client 的請求
4. server: 添加 HttpResponseEncoder 用來發送響應給 client
### [](https://github.com/waylau/essential-netty-in-action/blob/master/CORE%20FUNCTIONS/Building%20Netty%20HTTPHTTPS%20applications.md#http消息聚合)HTTP消息聚合
安裝 ChannelPipeline 中的初始化之后,你能夠對不同 HttpObject 消息進行操作。但由于 HTTP 請求和響應可以由許多部分組合而成,你需要聚合他們形成完整的消息。為了消除這種繁瑣任務, Netty 提供了一個聚合器,合并消息部件到 FullHttpRequest 和 FullHttpResponse 消息。這樣您總是能夠看到完整的消息內容。
這個操作有一個輕微的成本,消息段需要緩沖,直到完全可以將消息轉發到下一個 ChannelInboundHandler 管道。但好處是,你不必擔心消息碎片。
實現自動聚合只需添加另一個 ChannelHandler 到 ChannelPipeline。清單8.3顯示了這是如何實現的。
Listing 8.3 Automatically aggregate HTTP message fragments
~~~
public class HttpAggregatorInitializer extends ChannelInitializer<Channel> {
private final boolean client;
public HttpAggregatorInitializer(boolean client) {
this.client = client;
}
@Override
protected void initChannel(Channel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
if (client) {
pipeline.addLast("codec", new HttpClientCodec()); //1
} else {
pipeline.addLast("codec", new HttpServerCodec()); //2
}
pipeline.addLast("aggegator", new HttpObjectAggregator(512 * 1024)); //3
}
}
~~~
1. client: 添加 HttpClientCodec
2. server: 添加 HttpServerCodec 作為我們是 server 模式時
3. 添加 HttpObjectAggregator 到 ChannelPipeline, 使用最大消息值是 512kb
### [](https://github.com/waylau/essential-netty-in-action/blob/master/CORE%20FUNCTIONS/Building%20Netty%20HTTPHTTPS%20applications.md#http-壓縮)HTTP 壓縮
使用 HTTP 時建議壓縮數據以減少傳輸流量,壓縮數據會增加 CPU 負載,現在的硬件設施都很強大,大多數時候壓縮數據時一個好主意。Netty 支持“gzip”和“deflate”,為此提供了兩個 ChannelHandler 實現分別用于壓縮和解壓。看下面代碼:
#### [](https://github.com/waylau/essential-netty-in-action/blob/master/CORE%20FUNCTIONS/Building%20Netty%20HTTPHTTPS%20applications.md#http-request-header)HTTP Request Header
客戶端可以通過提供下面的頭顯示支持加密模式。然而服務器不是,所以不得不壓縮它發送的數據。
~~~
GET /encrypted-area HTTP/1.1
Host: www.example.com
Accept-Encoding: gzip, deflate
~~~
下面是一個例子
Listing 8.4 Automatically compress HTTP messages
~~~
public class HttpAggregatorInitializer extends ChannelInitializer<Channel> {
private final boolean isClient;
public HttpAggregatorInitializer(boolean isClient) {
this.isClient = isClient;
}
@Override
protected void initChannel(Channel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
if (isClient) {
pipeline.addLast("codec", new HttpClientCodec()); //1
pipeline.addLast("decompressor",new HttpContentDecompressor()); //2
} else {
pipeline.addLast("codec", new HttpServerCodec()); //3
pipeline.addLast("compressor",new HttpContentCompressor()); //4
}
}
}
~~~
1. client: 添加 HttpClientCodec
2. client: 添加 HttpContentDecompressor 用于處理來自服務器的壓縮的內容
3. server: HttpServerCodec
4. server: HttpContentCompressor 用于壓縮來自 client 支持的 HttpContentCompressor
*壓縮與依賴*
*注意,Java 6或者更早版本,如果要壓縮數據,需要添加?[jzlib](http://www.jcraft.com/jzlib/)?到 classpath*
~~~
<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jzlib</artifactId>
<version>1.1.3</version>
</dependency>
~~~
### [](https://github.com/waylau/essential-netty-in-action/blob/master/CORE%20FUNCTIONS/Building%20Netty%20HTTPHTTPS%20applications.md#使用-https)使用 HTTPS
啟用 HTTPS,只需添加 SslHandler
Listing 8.5 Using HTTPS
~~~
public class HttpsCodecInitializer extends ChannelInitializer<Channel> {
private final SslContext context;
private final boolean client;
public HttpsCodecInitializer(SslContext context, boolean client) {
this.context = context;
this.client = client;
}
@Override
protected void initChannel(Channel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
SSLEngine engine = context.newEngine(ch.alloc());
pipeline.addFirst("ssl", new SslHandler(engine)); //1
if (client) {
pipeline.addLast("codec", new HttpClientCodec()); //2
} else {
pipeline.addLast("codec", new HttpServerCodec()); //3
}
}
}
~~~
1. 添加 SslHandler 到 pipeline 來啟用 HTTPS
2. client: 添加 HttpClientCodec
3. server: 添加 HttpServerCodec ,如果是 server 模式的話
上面的代碼就是一個很好的例子,解釋了 Netty 的架構是如何讓“重用”變成了“杠桿”。我們可以添加一個新的功能,甚至是一樣重要的加密支持,幾乎沒有工作量,只需添加一個ChannelHandler 到 ChannelPipeline。
### [](https://github.com/waylau/essential-netty-in-action/blob/master/CORE%20FUNCTIONS/Building%20Netty%20HTTPHTTPS%20applications.md#websocket)WebSocket
HTTP 是不錯的協議,但是如果需要實時發布信息怎么做?有個做法就是客戶端一直輪詢請求服務器,這種方式雖然可以達到目的,但是其缺點很多,也不是優秀的解決方案,為了解決這個問題,便出現了 WebSocket。
WebSocket 允許數據雙向傳輸,而不需要請求-響應模式。早期的WebSocket 只能發送文本數據,然后現在不僅可以發送文本數據,也可以發送二進制數據,這使得可以使用 WebSocket 構建你想要的程序。下圖是WebSocket 的通信示例圖:
WebSocket 規范及其實現是為了一個更有效的解決方案。簡單的說, 一個WebSocket 提供一個 TCP 連接兩個方向的交通。結合 WebSocket API 它提供了一個替代 HTTP 輪詢雙向通信從頁面到遠程服務器。
也就是說,WebSocket 提供真正的雙向客戶機和服務器之間的數據交換。 我們不會對內部太多的細節,但我們應該提到,雖然最早實現僅限于文本數據,但現在不再是這樣,WebSocket可以用于任意數據,就像一個正常的套接字。
圖8.4給出了一個通用的 WebSocket 協議。在這種情況下的通信開始于普通 HTTP ,并“升級”為雙向 WebSocket。
[](https://github.com/waylau/essential-netty-in-action/blob/master/iamges/Figure%208.4%20WebSocket%20protocol.jpg)
1. Client (HTTP) 與 Server 通訊
2. Server (HTTP) 與 Client 通訊
3. Client 通過 HTTP(s) 來進行 WebSocket 握手,并等待確認
4. 連接協議升級至 WebSocket
Figure 8.4 WebSocket protocol
添加應用程序支持 WebSocket 只需要添加適當的客戶端或服務器端WebSocket ChannelHandler 到管道。這個類將處理特殊 WebSocket 定義的消息類型,稱為“幀。“如表8.3所示,這些可以歸類為“數據”和“控制”幀。
Table 8.3 WebSocketFrame types
| 名稱 | 描述 |
| --- | --- |
| BinaryWebSocketFrame | Data frame: binary data |
| TextWebSocketFrame | Data frame: text data |
| ContinuationWebSocketFrame | Data frame: text or binary data that belongs to a previous BinaryWebSocketFrame or TextWebSocketFrame |
| CloseWebSocketFrame | Control frame: a CLOSE request, close status code and a phrase |
| PingWebSocketFrame | Control frame: requests the send of a PongWebSocketFrame |
| PongWebSocketFrame | Control frame: sent as response to a PingWebSocketFrame |
由于 Netty 的主要是一個服務器端技術重點在這里創建一個 WebSocket server 。清單8.6使用 WebSocketServerProtocolHandler 提出了一個簡單的例子。該類處理協議升級握手以及三個“控制”幀 Close, Ping 和 Pong。Text 和 Binary 數據幀將被傳遞到下一個處理程序(由你實現)進行處理。
Listing 8.6 Support WebSocket on the server
~~~
public class WebSocketServerInitializer extends ChannelInitializer<Channel> {
@Override
protected void initChannel(Channel ch) throws Exception {
ch.pipeline().addLast(
new HttpServerCodec(),
new HttpObjectAggregator(65536), //1
new WebSocketServerProtocolHandler("/websocket"), //2
new TextFrameHandler(), //3
new BinaryFrameHandler(), //4
new ContinuationFrameHandler()); //5
}
public static final class TextFrameHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {
@Override
public void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception {
// Handle text frame
}
}
public static final class BinaryFrameHandler extends SimpleChannelInboundHandler<BinaryWebSocketFrame> {
@Override
public void channelRead0(ChannelHandlerContext ctx, BinaryWebSocketFrame msg) throws Exception {
// Handle binary frame
}
}
public static final class ContinuationFrameHandler extends SimpleChannelInboundHandler<ContinuationWebSocketFrame> {
@Override
public void channelRead0(ChannelHandlerContext ctx, ContinuationWebSocketFrame msg) throws Exception {
// Handle continuation frame
}
}
}
~~~
1. 添加 HttpObjectAggregator 用于提供在握手時聚合 HttpRequest
2. 添加 WebSocketServerProtocolHandler 用于處理色好給你寄握手如果請求是發送到"/websocket." 端點,當升級完成后,它將會處理Ping, Pong 和 Close 幀
3. TextFrameHandler 將會處理 TextWebSocketFrames
4. BinaryFrameHandler 將會處理 BinaryWebSocketFrames
5. ContinuationFrameHandler 將會處理ContinuationWebSocketFrames
*加密 WebSocket*?*只需插入 SslHandler 到作為 pipline 第一個 ChannelHandler*
詳見?[Chapter 11 WebSocket](https://github.com/waylau/essential-netty-in-action/blob/master/NETTY%20BY%20EXAMPLE/WebSockets.md)
### [](https://github.com/waylau/essential-netty-in-action/blob/master/CORE%20FUNCTIONS/Building%20Netty%20HTTPHTTPS%20applications.md#spdy)SPDY
[SPDY](http://www.chromium.org/spdy)(讀作“SPeeDY”)是Google 開發的基于 TCP 的應用層協議,用以最小化網絡延遲,提升網絡速度,優化用戶的網絡使用體驗。SPDY 并不是一種用于替代 HTTP 的協議,而是對 HTTP 協議的增強。SPDY 實現技術:
* 壓縮報頭
* 加密所有
* 多路復用連接
* 提供支持不同的傳輸優先級
SPDY 主要有5個版本:
* 1 - 初始化版本,但沒有使用
* 2 - 新特性,包含服務器推送
* 3 - 新特性包含流控制和更新壓縮
* 3.1 - 會話層流程控制
* 4.0 - 流量控制,并與 HTTP 2.0 更加集成
SPDY 被很多瀏覽器支持,包括 Google Chrome, Firefox, 和 Opera
Netty 支持 版本 2 和 3 (包含3.1)的支持。這些版本被廣泛應用,可以支持更多的用戶。更多內容詳見?[Chapter 12](https://github.com/waylau/essential-netty-in-action/blob/master/NETTY%20BY%20EXAMPLE/SPDY.md)
- Introduction
- 開始
- Netty-異步和數據驅動
- Netty 介紹
- 構成部分
- 關于本書
- 第一個 Netty 應用
- 設置開發環境
- Netty 客戶端/服務端 總覽
- 寫一個 echo 服務器
- 寫一個 echo 客戶端
- 編譯和運行 Echo 服務器和客戶端
- 總結
- Netty 總覽
- Netty 快速入門
- Channel, Event 和 I/O
- 什么是 Bootstrapping 為什么要用
- ChannelHandler 和 ChannelPipeline
- 近距離觀察 ChannelHandler
- 總結
- 核心功能
- Transport(傳輸)
- 案例研究:Transport 的遷移
- Transport API
- 包含的 Transport
- Transport 使用情況
- 總結
- Buffer(緩沖)
- Buffer API
- ByteBuf - 字節數據的容器
- 字節級別的操作
- ByteBufHolder
- ByteBuf 分配
- 總結
- ChannelHandler 和 ChannelPipeline
- ChannelHandler 家族
- ChannelPipeline
- ChannelHandlerContext
- 總結
- Codec 框架
- 什么是 Codec
- Decoder(解碼器)
- Encoder(編碼器)
- 抽象 Codec(編解碼器)類
- 總結
- 提供了的 ChannelHandler 和 Codec
- 使用 SSL/TLS 加密 Netty 程序
- 構建 Netty HTTP/HTTPS 應用
- 空閑連接以及超時
- 解碼分隔符和基于長度的協議
- 編寫大型數據
- 序列化數據
- 總結
- Bootstrap 類型
- 引導客戶端和無連接協議
- 引導服務器
- 從 Channel 引導客戶端
- 在一個引導中添加多個 ChannelHandler
- 使用Netty 的 ChannelOption 和屬性
- 關閉之前已經引導的客戶端或服務器
- 總結
- 引導
- Bootstrap 類型
- 引導客戶端和無連接協議
- 引導服務器
- 從 Channel 引導客戶端
- 在一個引導中添加多個 ChannelHandler
- 使用Netty 的 ChannelOption 和屬性
- 關閉之前已經引導的客戶端或服務器
- 總結
- NETTY BY EXAMPLE
- 單元測試
- 總覽
- 測試 ChannelHandler
- 測試異常處理
- 總結
- WebSocket
- WebSocket 程序示例
- 添加 WebSocket 支持
- 測試程序
- 總結
- SPDY
- SPDY 背景
- 示例程序
- 實現
- 啟動 SpdyServer 并測試
- 總結
- 通過 UDP 廣播事件
- UDP 基礎
- UDP 廣播
- UDP 示例
- EventLog 的 POJO
- 寫廣播器
- 寫監視器
- 運行 LogEventBroadcaster 和 LogEventMonitor
- 總結
- 高級主題
- 實現自定義的編解碼器
- 編解碼器的范圍
- 實現 Memcached 編解碼器
- 了解 Memcached 二進制協議
- Netty 編碼器和解碼器
- 測試編解碼器
- EventLoop 和線程模型
- 線程模型的總覽
- EventLoop
- EventLoop
- I/O EventLoop/Thread 分配細節
- 總結
- 用例1:Droplr Firebase 和 Urban Airship
- 用例2:Facebook 和 Twitter