ChannelInitializer在Netty中是一個很重要的東西。也是4.x版本中用戶接觸比較多的一個類
它本身是繼承ChannelInboundHandlerAdapter的。實現ChannelInboundHandler類
【推薦1】Netty4 ChannelPipeLine分析 ★★★★☆
【推薦2】java netty之ChannelPipeline ★★★☆☆
【推薦3】netty源碼分析之FrameDecoder(LengthFieldBasedFrameDecoder) ★★★☆☆
ChannelPipeline 中的中的事件流程
-
I/O Request via
Channel
orChannelHandlerContext
| +---------------------------------------------------+---------------+ | ChannelPipeline | | | \|/ | | +---------------------+ +-----------+----------+ | | | Inbound Handler N | | Outbound Handler 1 | | | +----------+----------+ +-----------+----------+ | | /|\ | | | | \|/ | | +----------+----------+ +-----------+----------+ | | | Inbound Handler N-1 | | Outbound Handler 2 | | | +----------+----------+ +-----------+----------+ | | /|\ . | | . . | | ChannelHandlerContext.fireIN_EVT() ChannelHandlerContext.OUT_EVT()| | [ method call] [method call] | | . . | | . \|/ | | +----------+----------+ +-----------+----------+ | | | Inbound Handler 2 | | Outbound Handler M-1 | | | +----------+----------+ +-----------+----------+ | | /|\ | | | | \|/ | | +----------+----------+ +-----------+----------+ | | | Inbound Handler 1 | | Outbound Handler M | | | +----------+----------+ +-----------+----------+ | | /|\ | | +---------------+-----------------------------------+---------------+ | \|/ +---------------+-----------------------------------+---------------+ | | | | | [ Socket.read() ] [ Socket.write() ] | | | | Netty Internal I/O Threads (Transport Implementation) | +-------------------------------------------------------------------+
這個圖是joc里面的ChannelPipeline文檔里面的一張示意圖。圖示的內容是。
當有IO請求ChannelInboundHandler 是從上之下搜索,相應的實現。一次執行。
4.x版本中的Netty。啟用了一個新的東西用於初始ChannelPipeline -> ChannelInitializer<Channel>ChannelInitializer有一個抽象未實現的方法:initChannel(C ch) 用於開發者自己定義相關的Handler添加到信道中。原理及詳細分析請參考上面推薦的兩篇文章。可能版本還不是最新的。但是4.x版本之后的一些大致的思路都是沒有什么變化。
下面把自己寫的小東西貼出來。1 package com.engine.netty.server.initializer; 2 3 import io.netty.channel.ChannelInitializer; 4 import io.netty.channel.ChannelPipeline; 5 import io.netty.channel.socket.SocketChannel; 6 import io.netty.handler.codec.LengthFieldBasedFrameDecoder; 7 import io.netty.handler.codec.LengthFieldPrepender; 8 import io.netty.handler.timeout.IdleStateHandler; 9 10 import com.engine.netty.server.handler.IdleStateCheckHandler; 11 import com.engine.netty.server.handler.codec.DecoderAMF3; 12 import com.engine.netty.server.handler.codec.EncoderAMF3; 13 import com.engine.netty.socket.ChatServerHandler; 14 15 /** 16 * Creates a newly configured {@link ChannelPipeline} for a new channel. 17 */ 18 public class NettyServerInitializer extends ChannelInitializer<SocketChannel> { 19 20 @Override 21 public void initChannel(SocketChannel ch) throws Exception { 22 ChannelPipeline pipeline = ch.pipeline(); 23 24 // 編碼或者解碼 拆包 25 /* 26 * 需要和客戶端訂立 相關通信協議底層規則 - 【消息長度(2 : short表示 2個字節)】【消息內容】 27 * lengthFieldLength => 是整數 - 占四個字節 所以同時偏移四個字節 => (0, 4, 0, 4) 28 * 29 * */ 30 pipeline.addLast("framer", new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 2, 0, 2)); 31 pipeline.addLast("prepender", new LengthFieldPrepender(4, false)); 32 33 // 空閑狀態檢查 間隔無消息,斷開連接 34 pipeline.addLast("idleStateCheck", new IdleStateHandler(0, 0, 600)); 35 pipeline.addLast("idleCheckHandler", new IdleStateCheckHandler()); 36 37 // AMF3 編碼或者解碼 38 pipeline.addLast("decoder", new DecoderAMF3()); 39 pipeline.addLast("encoder", new EncoderAMF3()); 40 41 // 序列化對象object編碼和解碼 【用於Java Object】 42 //pipeline.addLast("objectEncoder", new ObjectEncoder()); 43 //pipeline.addLast("objectDecoder", new ObjectDecoder(ClassResolvers.cacheDisabled(null))); 44 45 /*// 以UTF-8格式編碼和解碼 數據 【用於字符串】 46 pipeline.addLast("decoder", new StringDecoder(CharsetUtil.UTF_8)); 47 pipeline.addLast("encoder", new StringEncoder(CharsetUtil.UTF_8));*/ 48 49 // and then business logic. 50 pipeline.addLast("handler", new ChatServerHandler()); 51 } 52 }
發出去AMF3的東西是上一篇記錄里面寫的之外。其他兩種編碼和解碼都是官網有相對應例子的。編碼和解碼說白其實就是對字節數據的翻譯。在Netty中數據已ByteBuf保存。ByteBuf提供了很多的方法讀取相對應的數據。在這里就不詳細介紹了。有興趣的可以去看看官網的示例。或者是API 文檔。文檔中有一些類。官方都提供了相對應的代碼范例。
【空閑狀態檢查】是官網API文檔中找到的。
io.netty.handler.timeout 這個包中就是Netty提供的一些關於讀超時,寫超時,讀寫超時。空閑時間等的東西。
LengthFieldBasedFrameDecoder 和 LengthFieldPrepender
這兩個編碼解碼 相當於是對數據的處理。
先講一下LengthFieldBasedFrameDecoder :
直接通過命名大家很明顯的可以理解這個類 一個根據幀長度的解碼類。到底是怎么樣子的呢?其實很簡單。
LengthFieldBasedFrameDecoder(int maxFrameLength, int lengthFieldOffset, int lengthFieldLength, int lengthAdjustment, int initialBytesToStrip)
參數:
maxFrameLength: the maximum length of the frame. If the length of the frame is greater than this value, TooLongFrameException will be thrown. (最大幀長)
lengthFieldOffset: the offset of the length field (偏移量)
lengthFieldLength: the length of the length field
lengthAdjustment: the compensation value to add to the value of the length field
initialBytesToStrip: the number of first bytes to strip out from the decoded frame
它有5個參數 (詳細的解釋可以參考推薦文章3).
說簡單點就是第一個是字節消息的最大長度。第二個參數是消息長度這個字節的偏移量。第三個參數是描述字節消息長度的那部分的長度。第四個不解釋。第五個是跳過首部的字節長度
我這邊寫的0202其實意思就是 我的消息長度是有一個short類型來描述的。short類型的數據占2個字節。第二個2是說在返回最終解碼的數據的時候跳過這個描述長度的字節數。
API文檔中的:
2 bytes length field at offset 0, strip header :
Because we can get the length of the content by calling ByteBuf.readableBytes(), you might want to strip the length field by specifying initialBytesToStrip. In this example, we specified 2, that is same with the length of the length field, to strip the first two bytes.
lengthFieldOffset = 0
lengthFieldLength = 2
lengthAdjustment = 0
initialBytesToStrip = 2 (= the length of the Length field)
BEFORE DECODE (14 bytes) AFTER DECODE (12 bytes)
+--------+----------------+ +----------------+
| Length | Actual Content |----->| Actual Content |
| 0x000C | "HELLO, WORLD" | | "HELLO, WORLD" |
+--------+----------------+ +----------------+
最后的ChatServerHandler是我自己寫的邏輯的Handler
貌似就寫到這邊了。O(∩_∩)O哈哈~