# ChannelHandlerContext
接口 ChannelHandlerContext 代表 ChannelHandler 和ChannelPipeline 之間的關聯,并在 ChannelHandler 添加到 ChannelPipeline 時創建一個實例。ChannelHandlerContext 的主要功能是管理通過同一個 ChannelPipeline 關聯的 ChannelHandler 之間的交互。
ChannelHandlerContext 有許多方法,其中一些也出現在 Channel 和ChannelPipeline 本身。然而,如果您通過Channel 或ChannelPipeline 的實例來調用這些方法,他們就會在整個 pipeline中傳播 。相比之下,一樣的的方法在 ChannelHandlerContext的實例上調用, 就只會從當前的 ChannelHandler 開始并傳播到相關管道中的下一個有處理事件能力的 ChannelHandler 。
ChannelHandlerContext API 總結如下:
Table 6.10 ChannelHandlerContext API
| 名稱 | 描述 |
|-----|-----|
| bind | Request to bind to the given SocketAddress and return a ChannelFuture. |
| channel | Return the Channel which is bound to this instance. |
| close | Request to close the Channel and return a ChannelFuture. |
| connect | Request to connect to the given SocketAddress and return a ChannelFuture. |
| deregister | Request to deregister from the previously assigned EventExecutor and return a ChannelFuture. |
| disconnect | Request to disconnect from the remote peer and return a ChannelFuture. |
| executor | Return the EventExecutor that dispatches events. |
| fireChannelActive | A Channel is active (connected). |
| fireChannelInactive | A Channel is inactive (closed). |
| fireChannelRead | A Channel received a message. |
| fireChannelReadComplete | Triggers a channelWritabilityChanged event to the next |
ChannelInboundHandler.handler | Returns the ChannelHandler bound to this instance.isRemoved | Returns true if the associated ChannelHandler was removed from the ChannelPipeline.name | Returns the unique name of this instance.pipeline | Returns the associated ChannelPipeline.read | Request to read data from the Channel into the first inbound buffer. Triggers a channelRead event if successful and notifies the handler of channelReadComplete.write | Request to write a message via this instance through the pipeline.
其他注意注意事項:
- ChannelHandlerContext 與 ChannelHandler 的關聯從不改變,所以緩存它的引用是安全的。
- 正如我們前面指出的,ChannelHandlerContext 所包含的事件流比其他類中同樣的方法都要短,利用這一點可以盡可能高地提高性能。
### 使用 ChannelHandler
本節,我們將說明 ChannelHandlerContext的用法 ,以及ChannelHandlerContext, Channel 和 ChannelPipeline 這些類中方法的不同表現。下圖展示了 ChannelPipeline, Channel,ChannelHandler 和 ChannelHandlerContext 的關系

1. Channel 綁定到 ChannelPipeline
1. ChannelPipeline 綁定到 包含 ChannelHandler 的 Channel
1. ChannelHandler
1. 當添加 ChannelHandler 到 ChannelPipeline 時,ChannelHandlerContext 被創建
Figure 6.3 Channel, ChannelPipeline, ChannelHandler and ChannelHandlerContext
下面展示了, 從 ChannelHandlerContext 獲取到 Channel 的引用,通過調用 Channel 上的 write() 方法來觸發一個 寫事件到通過管道的的流中
Listing 6.6 Accessing the Channel from a ChannelHandlerContext
~~~
ChannelHandlerContext ctx = context;
Channel channel = ctx.channel(); //1
channel.write(Unpooled.copiedBuffer("Netty in Action",
CharsetUtil.UTF_8)); //2
~~~
1. 得到與 ChannelHandlerContext 關聯的 Channel 的引用
1. 通過 Channel 寫緩存
下面展示了 從 ChannelHandlerContext 獲取到 ChannelPipeline 的相同示例
Listing 6.7 Accessing the ChannelPipeline from a ChannelHandlerContext
~~~
ChannelHandlerContext ctx = context;
ChannelPipeline pipeline = ctx.pipeline(); //1
pipeline.write(Unpooled.copiedBuffer("Netty in Action", CharsetUtil.UTF_8)); //2
~~~
1. 得到與 ChannelHandlerContext 關聯的 ChannelPipeline 的引用
1. 通過 ChannelPipeline 寫緩沖區
流在兩個清單6.6和6.7是一樣的,如圖6.4所示。重要的是要注意,雖然在 Channel 或者 ChannelPipeline 上調用write() 都會把事件在整個管道傳播,但是在 ChannelHandler 級別上,從一個處理程序轉到下一個卻要通過在 ChannelHandlerContext 調用方法實現。

1. 事件傳遞給 ChannelPipeline 的第一個 ChannelHandler
1. ChannelHandler 通過關聯的 ChannelHandlerContext 傳遞事件給 ChannelPipeline 中的 下一個
1. ChannelHandler 通過關聯的 ChannelHandlerContext 傳遞事件給 ChannelPipeline 中的 下一個
Figure 6.4 Event propagation via the Channel or the ChannelPipeline
為什么你可能會想從 ChannelPipeline 一個特定的點開始傳播一個事件?
- 通過減少 ChannelHandler 不感興趣的事件的傳遞,從而減少開銷
- 排除掉特定的對此事件感興趣的處理程序的處理
想要實現從一個特定的 ChannelHandler 開始處理,你必須引用與 此ChannelHandler的前一個ChannelHandler 關聯的 ChannelHandlerContext 。這個ChannelHandlerContext 將會調用與自身關聯的 ChannelHandler 的下一個ChannelHandler 。
下面展示了使用場景
Listing 6.8 Events via ChannelPipeline
~~~
ChannelHandlerContext ctx = context;
ctx.write(Unpooled.copiedBuffer("Netty in Action", CharsetUtil.UTF_8));
~~~
1. 獲得 ChannelHandlerContext 的引用
1. write() 將會把緩沖區發送到下一個 ChannelHandler
如下所示,消息將會從下一個ChannelHandler開始流過 ChannelPipeline ,繞過所有在它之前的ChannelHandler。

1. ChannelHandlerContext 方法調用
1. 事件發送到了下一個 ChannelHandler
1. 經過最后一個ChannelHandler后,事件從 ChannelPipeline 移除
Figure 6.5 Event flow for operations triggered via the ChannelHandlerContext
我們剛剛描述的用例是一種常見的情形,當我們想要調用某個特定的 ChannelHandler操作時,它尤其有用。
### ChannelHandler 和 ChannelHandlerContext 的高級用法
正如我們在清單6.6中看到的,通過調用ChannelHandlerContext的 pipeline() 方法,你可以得到一個封閉的 ChannelPipeline 引用。這使得可以在運行時操作 pipeline 的 ChannelHandler ,這一點可以被利用來實現一些復雜的需求,例如,添加一個 ChannelHandler 到 pipeline 來支持動態協議改變。
其他高級用例可以實現通過保持一個 ChannelHandlerContext 引用供以后使用,這可能發生在任何 ChannelHandler 方法,甚至來自不同的線程。清單6.9顯示了此模式被用來觸發一個事件。
Listing 6.9 ChannelHandlerContext usage
~~~
public class WriteHandler extends ChannelHandlerAdapter {
private ChannelHandlerContext ctx;
@Override
public void handlerAdded(ChannelHandlerContext ctx) {
this.ctx = ctx; //1
}
public void send(String msg) {
ctx.writeAndFlush(msg); //2
}
}
~~~
1. 存儲 ChannelHandlerContext 的引用供以后使用
1. 使用之前存儲的 ChannelHandlerContext 來發送消息
因為 ChannelHandler 可以屬于多個 ChannelPipeline ,它可以綁定多個 ChannelHandlerContext 實例。然而,ChannelHandler 用于這種用法必須添加 `@Sharable` 注解。否則,試圖將它添加到多個ChannelPipeline 將引發一個異常。此外,它必須既是線程安全的又能安全地使用多個同時的通道(比如,連接)。
清單6.10顯示了此模式的正確實現。
Listing 6.10 A shareable ChannelHandler
~~~
@ChannelHandler.Sharable //1
public class SharableHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
System.out.println("channel read message " + msg);
ctx.fireChannelRead(msg); //2
}
}
~~~
1. 添加 @Sharable 注解
1. 日志方法調用, 并專遞到下一個 ChannelHandler
上面這個 ChannelHandler 實現符合所有包含在多個管道的要求;它通過`@Sharable` 注解,并不持有任何狀態。而下面清單6.11中列出的情況則恰恰相反,它會造成問題。
Listing 6.11 Invalid usage of @Sharable
~~~
@ChannelHandler.Sharable //1
public class NotSharableHandler extends ChannelInboundHandlerAdapter {
private int count;
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
count++; //2
System.out.println("inboundBufferUpdated(...) called the "
+ count + " time"); //3
ctx.fireChannelRead(msg);
}
}
~~~
1. 添加 @Sharable
1. count 字段遞增
1. 日志方法調用, 并專遞到下一個 ChannelHandler
這段代碼的問題是它持有狀態:一個實例變量保持了方法調用的計數。將這個類的一個實例添加到 ChannelPipeline 并發訪問通道時很可能產生錯誤。(當然,這個簡單的例子中可以通過在 channelRead() 上添加 synchronized 來糾正 )
總之,使用`@Sharable`的話,要確定 ChannelHandler 是線程安全的。
*為什么共享 ChannelHandler*
*常見原因是要在多個 ChannelPipelines 上安裝一個 ChannelHandler 以此來實現跨多個渠道收集統計數據的目的。*
我們的討論 ChannelHandlerContext 及與其他框架組件關系的 到此結束。接下來我們將解析 Channel 狀態模型,準備仔細看看ChannelHandler 本身。
- 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