Netty 自帶了一些傳輸協議的實現,雖然沒有支持所有的傳輸協議,但是其自帶的已足夠我們來使用。Netty應用程序的傳輸協議依賴于底層協議,本節我們將學習Netty中的傳輸協議。
Netty中的傳輸方式有如下幾種:
Table 4.1 Provided transports
| 方法名稱 | 包 | 描述 |
| --- | --- | --- |
| NIO | io.netty.channel.socket.nio | 基于java.nio.channels的工具包,使用選擇器作為基礎的方法。 |
| OIO | io.netty.channel.socket.oio | 基于java.net的工具包,使用阻塞流。 |
| Local | io.netty.channel.local | 用來在虛擬機之間本地通信。 |
| Embedded | io.netty.channel.embedded | 嵌入傳輸,它允許在沒有真正網絡的運輸中使用 ChannelHandler,可以非常有用的來測試ChannelHandler的實現。 |
### [](https://github.com/waylau/essential-netty-in-action/blob/master/CORE%20FUNCTIONS/Included%20transports.md#nio-nonblocking-io)NIO-Nonblocking I/O
NIO傳輸是目前最常用的方式,它通過使用選擇器提供了完全異步的方式操作所有的 I/O,NIO 從Java 1.4才被提供。
NIO 中,我們可以注冊一個通道或獲得某個通道的改變的狀態,通道狀態有下面幾種改變:
* 一個新的 Channel 被接受并已準備好
* Channel 連接完成
* Channel 中有數據并已準備好讀取
* Channel 發送數據出去
處理完改變的狀態后需重新設置他們的狀態,用一個線程來檢查是否有已準備好的 Channel,如果有則執行相關事件。在這里可能只同時一個注冊的事件而忽略其他的。選擇器所支持的操作在 SelectionKey 中定義,具體如下:
Table 4.2 Selection operation bit-set
| 方法名稱 | 描述 |
| --- | --- |
| OP_ACCEPT | 有新連接時得到通知 |
| OP_CONNECT | 連接完成后得到通知 |
| OP_REA | 準備好讀取數據時得到通知 |
| OP_WRITE | 寫入更多數據到通道時得到通知,大部分時間 |
這是可能的,但有時 socket 緩沖區完全填滿了。這通常發生在你寫數據的速度太快了超過了遠程節點的處理能力。
Figure 4.2 Selecting and Processing State Changes
[](https://github.com/waylau/essential-netty-in-action/blob/master/images/Figure%204.2%20Selecting%20and%20Processing%20State%20Changes.jpg)
1.新信道注冊 WITH 選擇器
2.選擇處理的狀態變化的通知
3.以前注冊的通道
4.Selector.select()方法阻塞,直到新的狀態變化接收或配置的超時 已過
5.檢查是否有狀態變化
6.處理所有的狀態變化
7.在選擇器操作的同一個線程執行其他任務
有一種功能,目前僅適用于 NIO 傳輸叫什么 “zero-file-copy (零文件拷貝)”,這使您能夠快速,高效地通過移動數據到從文件系統傳輸內容 網絡協議棧而無需復制從內核空間到用戶空間。這可以使 FT P或 HTTP 協議有很大的不同。
然而,并非所有的操作系統都支持此功能。此外,你不能用它實現數據加密或壓縮文件系統 - 僅支持文件的原生內容。另一方面,傳送的文件原本已經加密的是完全有效的。
接下來,我們將討論的是 OIO ,它提供了一個阻塞傳輸。
### [](https://github.com/waylau/essential-netty-in-action/blob/master/CORE%20FUNCTIONS/Included%20transports.md#oio-old-blocking-io)OIO-Old blocking I/O
Netty 中,該 OIO 傳輸代表了一種妥協。它通過了 Netty 的通用 API 訪問但不是異步,而是構建在 java.net 的阻塞實現上。任何人下面討論這一點可能會認為,這個協議并沒有很大優勢。但它確實有它有效的用途。
假設你需要的端口使用該做阻塞調用庫(例如?[JDBC](http://www.oracle.com/technetwork/java/javase/jdbc/index.html))。它可能不適合非阻塞。相反,你可以在短期內使用 OIO 傳輸,后來移植到純異步的傳輸上。讓我們看看它是如何工作的。
在 java.net API,你通常有一個線程接受新的連接到達監聽在ServerSocket,并創建一個新的線程來處理新的 Socket 。這是必需的,因為在一個特定的 socket的每個 I/O 操作可能會阻塞在任何時間。在一個線程處理多個 socket 易造成阻塞操作,一個 socket 占用了所有的其他人。
鑒于此,你可能想知道 Netty 是如何用相同的 API 來支持 NIO 的異步傳輸。這里的 Netty 利用了 SO_TIMEOUT 標志,可以設置在一個 Socket。這 timeout 指定最大 毫秒 數量 用于等待 I/O 的操作完成。如果操作在指定的時間內失敗,SocketTimeoutException 會被拋出。 Netty中捕獲該異常并繼續處理循環。在接下來的事件循環運行,它再次嘗試。像 Netty 的異步架構來支持 OIO 的話,這其實是唯一的辦法。當SocketTimeoutException 拋出時,執行 stack trace。
Figure 4.3 OIO-Processing logic
[](https://github.com/waylau/essential-netty-in-action/blob/master/images/Figure%204.3%20OIO-Processing%20logic.jpg)
1.線程分配給 Socket
2.Socket 連接到遠程
3.讀操作(可能會阻塞)
4.讀完成
5.處理可讀的字節
6.執行提交到 socket 的其他任務
7.再次嘗試讀
### [](https://github.com/waylau/essential-netty-in-action/blob/master/CORE%20FUNCTIONS/Included%20transports.md#本地-transport-與-jvm-交互)本地 Transport 與 JVM 交互
Netty 提供了“本地”運輸,為運行在同一個 Java 虛擬機上的服務器和客戶之間提供異步通信。此傳輸支持所有的 Netty 常見的傳輸實現的 API。
在此傳輸中,與服務器 Channel 關聯的 SocketAddress 不是“綁定”到一個物理網絡地址中,而它被存儲在注冊表中,只要服務器是運行的。當 Channel 關閉時它會注銷。由于傳輸不接受“真正的”網絡通信,它不能與其他傳輸實現互操作。因此,客戶端是希望連接到使用當地的交通必須使用它,以及一個服務器。除此限制之外,它的使用是與其他的傳輸是相同的。
### [](https://github.com/waylau/essential-netty-in-action/blob/master/CORE%20FUNCTIONS/Included%20transports.md#內嵌-transport)內嵌 Transport
Netty中 還提供了可以嵌入 ChannelHandler 實例到其他的 ChannelHandler 的傳輸,使用它們就像輔助類,增加了靈活性的方法,使您可以與你的 ChannelHandler 互動。
該嵌入技術通常用于測試 ChannelHandler 的實現,但它也可用于將功能添加到現有的 ChannelHandler 而無需更改代碼。嵌入傳輸的關鍵是Channel 的實現,稱為“EmbeddedChannel”。
第10章描述了使用 EmbeddedChannel 來測試 ChannelHandlers。
- 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