天天看點

netty系列之:内置的Frame detection

目錄

  • 簡介
  • Frame detection
    • DelimiterBasedFrameDecoder
    • FixedLengthFrameDecoder
    • LengthFieldBasedFrameDecoder
    • LineBasedFrameDecoder
  • 總結

上篇文章我們講到了netty中怎麼自定義編碼和解碼器,但是自定義實作起來還是挺複雜的,一般沒有特殊必要的情況下,大家都希望越簡單越好,其難點就是找到ByteBuf中的分割點,将ByteBuf分割成為一個個的可以處理的單元。今天本文講講netty中自帶的分割處理機制。

在上一章,我們提到了需要有一種手段來區分ByteBuf中不同的資料,也就是說找到ByteBuf中不同資料的分割點。如果首先将ByteBuf分割成一個個的獨立的ByteBuf,再對獨立的ByteBuf進行處理就會簡單很多。

netty中提供了4個分割點的編碼器,我們可以稱之為Frame detection,他們分别是DelimiterBasedFrameDecoder, FixedLengthFrameDecoder, LengthFieldBasedFrameDecoder, 和 LineBasedFrameDecoder。

這幾個類都是ByteToMessageDecoder的子類,接下來我們一一進行介紹。

首先是DelimiterBasedFrameDecoder,看名字就知道這個是根據delimiter對bytebuf進行分割的解碼器。什麼是delimiter呢?

netty中有一個Delimiters類,專門定義分割的字元,主要有兩個delimiter,分别是nulDelimiter和lineDelimiter:

public static ByteBuf[] nulDelimiter() {             return new ByteBuf[] {                     Unpooled.wrappedBuffer(new byte[] { 0 }) };         }         public static ByteBuf[] lineDelimiter() {             return new ByteBuf[] {                     Unpooled.wrappedBuffer(new byte[] { '\r', '\n' }),                     Unpooled.wrappedBuffer(new byte[] { '\n' }),             };         }           

nullDelimiter用來處理0x00,主要用來處理Flash XML socket或者其他的類似的協定。

lineDelimiter用來處理回車和換行符,主要用來文本檔案的進行中。

對于DelimiterBasedFrameDecoder來說,如果有多個delimiter的話,會選擇将ByteBuf分割最短的那個,舉個例子,如果我們使用DelimiterBasedFrameDecoder(Delimiters.lineDelimiter()) ,因為lineDelimiter中實際上有兩個分割方式,回車+換行或者換行,如果遇到下面的情況:

+--------------+        | ABC\nDEF\r\n |        +--------------+           

DelimiterBasedFrameDecoder會選擇最短的分割結果,也就說将上面的内容分割成為:

+-----+-----+        | ABC | DEF |        +-----+-----+           

而不是

+----------+        | ABC\nDEF |        +----------+           

這個類會将ByteBuf分成固定的長度,比如收到了下面的4塊byte資訊:

+---+----+------+----+        | A | BC | DEFG | HI |        +---+----+------+----+           

如果使用一個FixedLengthFrameDecoder(3) ,則會将上面的ByteBuf分成下面的幾個部分:

+-----+-----+-----+        | ABC | DEF | GHI |        +-----+-----+-----+           

這個類就更加靈活一點,可以根據資料中的length字段取出後續的byte數組。LengthFieldBasedFrameDecoder非常靈活,它有4個屬性來控制他們分别是lengthFieldOffset、lengthFieldLength、lengthAdjustment和initialBytesToStrip。

lengthFieldOffset是長度字段的起始位置,lengthFieldLength是長度字段本身的長度,lengthAdjustment是對目标資料長度進行調整,initialBytesToStrip是解密過程中需要删除的byte數目。了解不了?沒關系,我們來舉幾個例子。

首先看一個最簡單的:

lengthFieldOffset   = 0        lengthFieldLength   = 2        lengthAdjustment    = 0        initialBytesToStrip = 0         BEFORE DECODE (14 bytes)         AFTER DECODE (14 bytes)        +--------+----------------+      +--------+----------------+        | Length | Actual Content |----->| Length | Actual Content |        | 0x000C | "HELLO, WORLD" |      | 0x000C | "HELLO, WORLD" |        +--------+----------------+      +--------+----------------+           

上面的設定表示,length是從第0位開始的,長度是2個位元組。其中Ox00C=12, 這也是“HELLO, WORLD” 的長度。

如果不想要Length字段,可以通過設定initialBytesToStrip把length删除:

lengthFieldOffset   = 0        lengthFieldLength   = 2        lengthAdjustment    = 0        initialBytesToStrip = 2 (= length 字段的長度)        BEFORE DECODE (14 bytes)         AFTER DECODE (12 bytes)        +--------+----------------+      +----------------+        | Length | Actual Content |----->| Actual Content |        | 0x000C | "HELLO, WORLD" |      | "HELLO, WORLD" |        +--------+----------------+      +----------------+           

lengthAdjustment是對Length字段的值進行調整,因為在有些情況下Length字段可能包含了整條資料的長度,也就是Length+内容,是以需要在解析的時候進行調整,比如下面的例子,真實長度其實是0x0C,但是傳入的卻是0x0E,是以需要減去Length字段的長度2,也就是将lengthAdjustment設定為-2。

lengthFieldOffset   =  0        lengthFieldLength   =  2        lengthAdjustment    = -2 (= Length字段的長度)        initialBytesToStrip =  0        BEFORE DECODE (14 bytes)         AFTER DECODE (14 bytes)        +--------+----------------+      +--------+----------------+        | Length | Actual Content |----->| Length | Actual Content |        | 0x000E | "HELLO, WORLD" |      | 0x000E | "HELLO, WORLD" |        +--------+----------------+      +--------+----------------+           

LineBasedFrameDecoder專門處理文本檔案中的一行結束。也就是 "\n" 和 "\r\n",他和DelimiterBasedFrameDecoder很類似,但是DelimiterBasedFrameDecoder更加通用。

有了上面4個Frame detection裝置之後,就可以在pipline中首先添加這些Frame detection,然後再添加自定義的handler,這樣在自定義的handler中就不用考慮讀取ByteBuf的長度問題了。

比如在StringDecoder中,如果已經使用了 LineBasedFrameDecoder , 那麼在decode方法中可以假設傳入的ByteBuf就是一行字元串,那麼可以直接這樣使用:

protected void decode(ChannelHandlerContext ctx, ByteBuf msg, List<Object> out) throws Exception {             out.add(msg.toString(charset));         }           

是不是很簡單?

本文已收錄于 http://www.flydean.com/15-netty-buildin-frame-detection/

最通俗的解讀,最深刻的幹貨,最簡潔的教程,衆多你不知道的小技巧等你來發現!

歡迎關注我的公衆号:「程式那些事」,懂技術,更懂你!