zoukankan      html  css  js  c++  java
  • Netty LengthFieldBasedFrameDecoder

    先看看LengthFieldBasedFrameDecoder的官方API 
    http://docs.jboss.org/netty/3.1/api/org/jboss/netty/handler/codec/frame/LengthFieldBasedFrameDecoder.html 

    API举例说明了LengthFieldBasedFrameDecoder的解析机制,如下: 

    实际消息内容是“HELLO, WORLD”,长度是12 bytes(注意逗号后面有一个空格) 

    实例1 
    lengthFieldLength   = 2表示“Length”的长度,而“Length”的值 
    就是“Actual Content”的长度(0x000C, 也就是12): 

    Java代码  收藏代码
    1. lengthFieldOffset   = 0  
    2. lengthFieldLength   = 2  
    3. lengthAdjustment    = 0  
    4. initialBytesToStrip = 0 (= do not strip header)  
    5.   
    6. BEFORE DECODE (14 bytes)         AFTER DECODE (14 bytes)  
    7. +--------+----------------+      +--------+----------------+  
    8. | Length | Actual Content |----->| Length | Actual Content |  
    9. 0x000C | "HELLO, WORLD" |      | 0x000C | "HELLO, WORLD" |  
    10. +--------+----------------+      +--------+----------------+  


    实例2 
    initialBytesToStrip = 2 表示在decode时,要去掉多少个字节 
    在这个例子,表示要去掉“Length”(2个字节) 
    可以看到,AFTER DECODE后,“Length”没有了,只剩下“Actual Content”: 
    Java代码  收藏代码
    1. lengthFieldOffset   = 0  
    2. lengthFieldLength   = 2  
    3. lengthAdjustment    = 0  
    4. initialBytesToStrip = 2 (= the length of the Length field)  
    5.   
    6. BEFORE DECODE (14 bytes)         AFTER DECODE (12 bytes)  
    7. +--------+----------------+      +----------------+  
    8. | Length | Actual Content |----->| Actual Content |  
    9. 0x000C | "HELLO, WORLD" |      | "HELLO, WORLD" |  
    10. +--------+----------------+      +----------------+  


    实例3 
    与实例1不同,这里“Length”的值不是“Actual Content”的长度,而是 
    整个消息的长度(0x000E,14  = 2  + 12)。用lengthAdjustment=-2来表示 
    “Actual Content”的长度要减2: 
    wholeLength = valueOf(Length) = 14 
    actualContentLength = wholeLength + lengthAdjustment = 14 + (-2)=12 
    Java代码  收藏代码
    1. lengthFieldOffset   =  0  
    2. lengthFieldLength   =  2  
    3. lengthAdjustment    = -2 (= the length of the Length field)  
    4. initialBytesToStrip =  0  
    5.   
    6. BEFORE DECODE (14 bytes)         AFTER DECODE (14 bytes)  
    7. +--------+----------------+      +--------+----------------+  
    8. | Length | Actual Content |----->| Length | Actual Content |  
    9. 0x000E | "HELLO, WORLD" |      | 0x000E | "HELLO, WORLD" |  
    10. +--------+----------------+      +--------+----------------+  


    实例4 
    这个例子多了一个“Header 1”(oxCAFE,长度为2 ) 
    用 lengthFieldOffset   = 2表示“Length”从第3个字节开始 
    “Length”的值仍然是“Actual Content ”的长度,不过“Length”自身的 
    长度是3 (值是0x00000C,而不是上面的0x000C) 
    Java代码  收藏代码
    1. lengthFieldOffset   = 2 (= the length of Header 1)  
    2. lengthFieldLength   = 3  
    3. lengthAdjustment    = 0  
    4. initialBytesToStrip = 0  
    5.   
    6. BEFORE DECODE (17 bytes)                      AFTER DECODE (17 bytes)  
    7. +----------+----------+----------------+      +----------+----------+----------------+  
    8. | Header 1 |  Length  | Actual Content |----->| Header 1 |  Length  | Actual Content |  
    9. |  0xCAFE  | 0x00000C | "HELLO, WORLD" |      |  0xCAFE  | 0x00000C | "HELLO, WORLD" |  
    10. +----------+----------+----------------+      +----------+----------+----------------+  


    实例5 
    与实例4不同的地方是,“Length”和“Header 1”的位置调换了 
    wholeLength = valueOf(Length) = 14 
    actualContentLength = wholeLength + lengthAdjustment = 14 + 2=16 
    因此在decode时,会认为“Header 1”也是“Actual Content”的一部分 
    Java代码  收藏代码
    1. lengthFieldOffset   = 0  
    2. lengthFieldLength   = 3  
    3. lengthAdjustment    = 2 (= the length of Header 1)  
    4. initialBytesToStrip = 0  
    5.   
    6. BEFORE DECODE (17 bytes)                      AFTER DECODE (17 bytes)  
    7. +----------+----------+----------------+      +----------+----------+----------------+  
    8. |  Length  | Header 1 | Actual Content |----->|  Length  | Header 1 | Actual Content |  
    9. 0x00000C |  0xCAFE  | "HELLO, WORLD" |      | 0x00000C |  0xCAFE  | "HELLO, WORLD" |  
    10. +----------+----------+----------------+      +----------+----------+----------------+  


    实例6 
    看起来要比之前的例子复杂一些,但其实是上面例子的组合 
    lengthFieldOffset = 1表示“Length”从第2个字节开始 
    lengthFieldLength   = 2表示“Length”的长度是2 
    lengthAdjustment    = 1表示“HDR2”的长度是1: 
    wholeLength = valueOf(Length) = 0x000C = 12 
    actualContentLength = wholeLength + lengthAdjustment = 12 + 1=13 
    decode时,会认为“Length”后面的13个字节都是“Actual Content”, 
    因此会认为“HDR2”也是“Actual Content”的一部分 
    initialBytesToStrip = 3 表示decode时,去掉3个字节 
    Java代码  收藏代码
    1. lengthFieldOffset   = 1 (= the length of HDR1)  
    2. lengthFieldLength   = 2  
    3. lengthAdjustment    = 1 (= the length of HDR2)  
    4. initialBytesToStrip = 3 (= the length of HDR1 + LEN)  
    5.   
    6. BEFORE DECODE (16 bytes)                       AFTER DECODE (13 bytes)  
    7. +------+--------+------+----------------+      +------+----------------+  
    8. | HDR1 | Length | HDR2 | Actual Content |----->| HDR2 | Actual Content |  
    9. 0xCA | 0x000C | 0xFE | "HELLO, WORLD" |      | 0xFE | "HELLO, WORLD" |  
    10. +------+--------+------+----------------+      +------+----------------+  

    实例7 
    与实例6不同的是,lengthAdjustment = -3,是负数 
    因此, 
    wholeLength = valueOf(Length) = 0x0010 = 16 
    actualContentLength = wholeLength + lengthAdjustment = 16 + (-3)=13 
    decode时,会认为“Length”后面的13个字节都是“Actual Content”, 
    最终效果与实例6一样 
    Java代码  收藏代码
    1. lengthFieldOffset   =  1  
    2. lengthFieldLength   =  2  
    3. lengthAdjustment    = -3 (= the length of HDR1 + LEN, negative)  
    4. initialBytesToStrip =  3  
    5.   
    6. BEFORE DECODE (16 bytes)                       AFTER DECODE (13 bytes)  
    7. +------+--------+------+----------------+      +------+----------------+  
    8. | HDR1 | Length | HDR2 | Actual Content |----->| HDR2 | Actual Content |  
    9. 0xCA | 0x0010 | 0xFE | "HELLO, WORLD" |      | 0xFE | "HELLO, WORLD" |  
    10. +------+--------+------+----------------+      +------+----------------+  


    API看完,我们来看看源码(只保留关键代码): 
    Java代码  收藏代码
    1. public class LengthFieldBasedFrameDecoder extends FrameDecoder {  
    2.   
    3.     private final int maxFrameLength;   //超出此长度的Frame将被丢弃  
    4.     private final int lengthFieldOffset;  
    5.     private final int lengthFieldLength;  
    6.     private final int lengthFieldEndOffset;     //这个值等于lengthFieldOffset + lengthFieldLength  
    7.     private final int lengthAdjustment;  
    8.     private final int initialBytesToStrip;  
    9.       
    10.     protected Object decode(  
    11.             ChannelHandlerContext ctx, Channel channel, ChannelBuffer buffer) throws Exception {  
    12.               
    13.         //数据未完整,先不处理  
    14.         if (buffer.readableBytes() < lengthFieldEndOffset) {  
    15.             return null;  
    16.         }  
    17.           
    18.         /* 
    19.         先读取“Length”的值 
    20.         在LengthFieldBasedFrameDecoder的构造函数中,限定了“Length”的长度: 
    21.         “lengthFieldLength must be either 1, 2, 3, 4, or 8” 
    22.         单位是bytes。这个限定不知从何而来,先不管 
    23.         由于接收到的数据的类型是ChannelBuffer,也就是byte[],那么在读取时, 
    24.         就应该根据长度来分割数据 
    25.         例如,lengthFieldLength=3,说明读取前3个字节就得到“Length”的值 
    26.         读取时,用到了位操作,ByteOrder是BIG_ENDIAN,因此高位在前,要左移位: 
    27.         public int getUnsignedMedium(int index) { 
    28.             return  (array[index]     & 0xff) << 16 | 
    29.                     (array[index + 1] & 0xff) <<  8 | 
    30.                     (array[index + 2] & 0xff) <<  0; 
    31.         } 
    32.         注意到,这个方法不会改变readerIndex 
    33.         下面代码的frameLength,其实就是上面API分析时提到的valueOf(Length) 
    34.         */  
    35.         int actualLengthFieldOffset = buffer.readerIndex() + lengthFieldOffset;  
    36.         long frameLength;  
    37.         switch (lengthFieldLength) {  
    38.         case 1:  
    39.             frameLength = buffer.getUnsignedByte(actualLengthFieldOffset);  
    40.             break;  
    41.         case 2:  
    42.             frameLength = buffer.getUnsignedShort(actualLengthFieldOffset);  
    43.             break;  
    44.         case 3:  
    45.             frameLength = buffer.getUnsignedMedium(actualLengthFieldOffset);  
    46.             break;  
    47.         case 4:  
    48.             frameLength = buffer.getUnsignedInt(actualLengthFieldOffset);  
    49.             break;  
    50.         case 8:  
    51.             frameLength = buffer.getLong(actualLengthFieldOffset);  
    52.             break;  
    53.         default:  
    54.             throw new Error("should not reach here");  
    55.         }  
    56.   
    57.         //如分析API时所说,要加上lengthAdjustment  
    58.         //那为什么 还要加上lengthFieldEndOffset?  
    59.         //加上之后,frameLength就代表整个frame的长度了,包括前缀和“Actual Content”  
    60.         frameLength += lengthAdjustment + lengthFieldEndOffset;  
    61.   
    62.         int frameLengthInt = (int) frameLength;  
    63.           
    64.         //数据未完整,先不处理  
    65.         if (buffer.readableBytes() < frameLengthInt) {  
    66.             return null;  
    67.         }  
    68.   
    69.         //readerIndex往后移,跳过指定的字节,不读取  
    70.         buffer.skipBytes(initialBytesToStrip);  
    71.   
    72.         // extract frame ,读取消息的内容  
    73.         int readerIndex = buffer.readerIndex();  
    74.         int actualFrameLength = frameLengthInt - initialBytesToStrip;  
    75.         ChannelBuffer frame = extractFrame(buffer, readerIndex, actualFrameLength);  
    76.           
    77.         //extractFrame方法不改变buffer的readerIndex,因此要手动设置  
    78.         buffer.readerIndex(readerIndex + actualFrameLength);  
    79.         return frame;  
    80.     }  
    81.       
    82.     //这个方法创建了新的ChannelBuffer,不影响原buffer  
    83.     protected ChannelBuffer extractFrame(ChannelBuffer buffer, int index, int length) {  
    84.         ChannelBuffer frame = buffer.factory().getBuffer(length);  
    85.         frame.writeBytes(buffer, index, length);  
    86.         return frame;  
    87.     }  
    88. }  
    89.       
    90.    

  • 相关阅读:
    git 常用命令
    spring源码-事件&监听3.6
    spring源码-国际化-3.5
    spring源码-Aware-3.4
    spring源码-BeanPostProcessor-3.3
    springboot中对yaml文件的解析
    数组Array.sort()排序的方法
    【转】js 对象按照键值(不分区大小写)排序,生成签名方法
    【转】JS常用函数整合库 lutils
    VS2017调试出现异常浏览器直接关闭的解决办法
  • 原文地址:https://www.cnblogs.com/hzcya1995/p/13318014.html
Copyright © 2011-2022 走看看