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

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

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

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

lengthFieldOffset   = 0lengthFieldLength   = 2lengthAdjustment    = 0initialBytesToStrip = 0 (= do not strip header)

BEFORE DECODE (14 bytes)         AFTER DECODE (14 bytes)+--------+----------------+      +--------+----------------+| Length | Actual Content |----->| Length | Actual Content || 0x000C | "HELLO, WORLD" |      | 0x000C | "HELLO, WORLD" |+--------+----------------+      +--------+----------------+

实例2
initialBytesToStrip = 2 表示在decode时,要去掉多少个字节
在这个例子,表示要去掉“Length”(2个字节)
可以看到,AFTER DECODE后,“Length”没有了,只剩下“Actual Content”:

lengthFieldOffset   = 0lengthFieldLength   = 2lengthAdjustment    = 0initialBytesToStrip = 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" |+--------+----------------+      +----------------+

实例3
与实例1不同,这里“Length”的值不是“Actual Content”的长度,而是
整个消息的长度(0x000E,14 = 2 + 12)。用lengthAdjustment=-2来表示
“Actual Content”的长度要减2:
wholeLength = valueOf(Length) = 14
actualContentLength = wholeLength + lengthAdjustment = 14 + (-2)=12

lengthFieldOffset   =  0lengthFieldLength   =  2lengthAdjustment    = -2 (= the length of the Length field)initialBytesToStrip =  0

BEFORE DECODE (14 bytes)         AFTER DECODE (14 bytes)+--------+----------------+      +--------+----------------+| Length | Actual Content |----->| Length | Actual Content || 0x000E | "HELLO, WORLD" |      | 0x000E | "HELLO, WORLD" |+--------+----------------+      +--------+----------------+

实例4
这个例子多了一个“Header 1”(oxCAFE,长度为2 )
用 lengthFieldOffset = 2表示“Length”从第3个字节开始
“Length”的值仍然是“Actual Content ”的长度,不过“Length”自身的
长度是3 (值是0x00000C,而不是上面的0x000C)

lengthFieldOffset   = 2 (= the length of Header 1)lengthFieldLength   = 3lengthAdjustment    = 0initialBytesToStrip = 0

BEFORE DECODE (17 bytes)                      AFTER DECODE (17 bytes)+----------+----------+----------------+      +----------+----------+----------------+| Header 1 |  Length  | Actual Content |----->| Header 1 |  Length  | Actual Content ||  0xCAFE  | 0x00000C | "HELLO, WORLD" |      |  0xCAFE  | 0x00000C | "HELLO, WORLD" |+----------+----------+----------------+      +----------+----------+----------------+

实例5
与实例4不同的地方是,“Length”和“Header 1”的位置调换了
wholeLength = valueOf(Length) = 14
actualContentLength = wholeLength + lengthAdjustment = 14 + 2=16
因此在decode时,会认为“Header 1”也是“Actual Content”的一部分

lengthFieldOffset   = 0lengthFieldLength   = 3lengthAdjustment    = 2 (= the length of Header 1)initialBytesToStrip = 0

BEFORE DECODE (17 bytes)                      AFTER DECODE (17 bytes)+----------+----------+----------------+      +----------+----------+----------------+|  Length  | Header 1 | Actual Content |----->|  Length  | Header 1 | Actual Content || 0x00000C |  0xCAFE  | "HELLO, WORLD" |      | 0x00000C |  0xCAFE  | "HELLO, WORLD" |+----------+----------+----------------+      +----------+----------+----------------+

实例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个字节

lengthFieldOffset   = 1 (= the length of HDR1)lengthFieldLength   = 2lengthAdjustment    = 1 (= the length of HDR2)initialBytesToStrip = 3 (= the length of HDR1 + LEN)

BEFORE DECODE (16 bytes)                       AFTER DECODE (13 bytes)+------+--------+------+----------------+      +------+----------------+| HDR1 | Length | HDR2 | Actual Content |----->| HDR2 | Actual Content || 0xCA | 0x000C | 0xFE | "HELLO, WORLD" |      | 0xFE | "HELLO, WORLD" |+------+--------+------+----------------+      +------+----------------+

实例7
与实例6不同的是,lengthAdjustment = -3,是负数
因此,
wholeLength = valueOf(Length) = 0x0010 = 16
actualContentLength = wholeLength + lengthAdjustment = 16 + (-3)=13
decode时,会认为“Length”后面的13个字节都是“Actual Content”,
最终效果与实例6一样

lengthFieldOffset   =  1lengthFieldLength   =  2lengthAdjustment    = -3 (= the length of HDR1 + LEN, negative)initialBytesToStrip =  3

BEFORE DECODE (16 bytes)                       AFTER DECODE (13 bytes)+------+--------+------+----------------+      +------+----------------+| HDR1 | Length | HDR2 | Actual Content |----->| HDR2 | Actual Content || 0xCA | 0x0010 | 0xFE | "HELLO, WORLD" |      | 0xFE | "HELLO, WORLD" |+------+--------+------+----------------+      +------+----------------+

API看完,我们来看看源码(只保留关键代码):

public class LengthFieldBasedFrameDecoder extends FrameDecoder {

    private final int maxFrameLength;  //超出此长度的Frame将被丢弃    private final int lengthFieldOffset;    private final int lengthFieldLength;    private final int lengthFieldEndOffset;      //这个值等于lengthFieldOffset + lengthFieldLength    private final int lengthAdjustment;    private final int initialBytesToStrip;

  protected Object decode(            ChannelHandlerContext ctx, Channel channel, ChannelBuffer buffer) throws Exception {

      //数据未完整,先不处理        if (buffer.readableBytes() < lengthFieldEndOffset) {            return null;        }

       /*        先读取“Length”的值     在LengthFieldBasedFrameDecoder的构造函数中,限定了“Length”的长度:     “lengthFieldLength must be either 1, 2, 3, 4, or 8”       单位是bytes。这个限定不知从何而来,先不管        由于接收到的数据的类型是ChannelBuffer,也就是byte[],那么在读取时,      就应该根据长度来分割数据      例如,lengthFieldLength=3,说明读取前3个字节就得到“Length”的值      读取时,用到了位操作,ByteOrder是BIG_ENDIAN,因此高位在前,要左移位:       public int getUnsignedMedium(int index) {         return  (array[index]     & 0xff) << 16 |                   (array[index + 1] & 0xff) <<  8 |                  (array[index + 2] & 0xff) <<  0;       }     注意到,这个方法不会改变readerIndex        下面代码的frameLength,其实就是上面API分析时提到的valueOf(Length)        */        int actualLengthFieldOffset = buffer.readerIndex() + lengthFieldOffset;        long frameLength;        switch (lengthFieldLength) {        case 1:            frameLength = buffer.getUnsignedByte(actualLengthFieldOffset);            break;        case 2:            frameLength = buffer.getUnsignedShort(actualLengthFieldOffset);            break;        case 3:            frameLength = buffer.getUnsignedMedium(actualLengthFieldOffset);            break;        case 4:            frameLength = buffer.getUnsignedInt(actualLengthFieldOffset);            break;        case 8:            frameLength = buffer.getLong(actualLengthFieldOffset);            break;        default:            throw new Error("should not reach here");        }

        //如分析API时所说,要加上lengthAdjustment        //那为什么 还要加上lengthFieldEndOffset?       //加上之后,frameLength就代表整个frame的长度了,包括前缀和“Actual Content”        frameLength += lengthAdjustment + lengthFieldEndOffset;

        int frameLengthInt = (int) frameLength;

        //数据未完整,先不处理        if (buffer.readableBytes() < frameLengthInt) {            return null;        }

     //readerIndex往后移,跳过指定的字节,不读取        buffer.skipBytes(initialBytesToStrip);

        // extract frame ,读取消息的内容        int readerIndex = buffer.readerIndex();        int actualFrameLength = frameLengthInt - initialBytesToStrip;        ChannelBuffer frame = extractFrame(buffer, readerIndex, actualFrameLength);

      //extractFrame方法不改变buffer的readerIndex,因此要手动设置        buffer.readerIndex(readerIndex + actualFrameLength);        return frame;    }

   //这个方法创建了新的ChannelBuffer,不影响原buffer    protected ChannelBuffer extractFrame(ChannelBuffer buffer, int index, int length) {        ChannelBuffer frame = buffer.factory().getBuffer(length);        frame.writeBytes(buffer, index, length);        return frame;    }}

Netty源码学习-LengthFieldBasedFrameDecoder相关推荐

  1. netty源码学习之服务端客户端初始化

    文章目录 1. AbstractBootstrap类简介 1.1. 核心方法 2. netty服务端创建 2.1. 服务端启动入口 2.2. doBind()方法 2.3. netty服务初始化 2. ...

  2. Netty源码学习(零)前言

    本系列文章将介绍Netty的工作机制,以及分析Netty的主要源码. 基于的版本是4.1.15.Final(2017.08.24发布) 水平有限,如有谬误请留言指正 参考资料 the_flash的简书 ...

  3. Netty源码分析系列之常用解码器(下)——LengthFieldBasedFrameDecoder

    扫描下方二维码或者微信搜索公众号菜鸟飞呀飞,即可关注微信公众号,Spring源码分析和Java并发编程文章. 前言 在上一篇文章中分析了三个比较简单的解码器,今天接着分析最后一个常用的解码器:Leng ...

  4. Netty 源码(ChannelHandler 死磕)

    精进篇:netty源码死磕5  - 揭开 ChannelHandler 的神秘面纱 目录 1. 前言 2. Handler在经典Reactor中的角色 3. Handler在Netty中的坐标位置 4 ...

  5. Netty源码分析第7章(编码器和写数据)----第2节: MessageToByteEncoder

    Netty源码分析第7章(编码器和写数据)---->第2节: MessageToByteEncoder Netty源码分析第七章: Netty源码分析 第二节: MessageToByteEnc ...

  6. RocketMQ源码学习

    RocketMQ源码学习 文章目录 RocketMQ源码学习 Producer 是怎么将消息发送至 Broker 的? 同步发送 异步发送 队列选择器 事务消息 原理 Broker 是怎么处理客户端发 ...

  7. RocketMQ 源码学习笔记 Producer 是怎么将消息发送至 Broker 的?

    RocketMQ 源码学习笔记 Producer 是怎么将消息发送至 Broker 的? 文章目录 RocketMQ 源码学习笔记 Producer 是怎么将消息发送至 Broker 的? 前言 项目 ...

  8. Netty源码解读(一)概述

    感谢网友[黄亿华]投递本稿. Netty和Mina是Java世界非常知名的通讯框架.它们都出自同一个作者,Mina诞生略早,属于Apache基金会,而Netty开始在Jboss名下,后来出来自立门户n ...

  9. JAVA NIO 简介 (netty源码死磕1.1)

    [基础篇]netty 源码死磕1.1:  JAVA NIO简介 1. JAVA NIO简介 Java 中 New I/O类库 是由 Java 1.4 引进的异步 IO.由于之前老的I/O类库是阻塞I/ ...

最新文章

  1. Tableau必知必会之图表显示部分标签的小妙招
  2. 分布式转码初步方案(hadoop+ffmpeg)
  3. 监控mysql主从复制监控_shell脚本监控mysql主从同步状态
  4. 暗黑破坏神(背包)(内部模拟)
  5. 论文浅尝 | GNN with Generated Parameters for Relation Extraction
  6. python语音属于什么语音_python语音识别
  7. 15.go install
  8. AngularJS 控制器 ng-controller
  9. 微型计算机ccc认证的流程,计算机的3C认证办理以及流程
  10. Docker(2) 安全加密,habor仓库和Docker网络
  11. 【深度学习】:详解目标检测YOLO V1(You Only Look Once)算法
  12. PMBOK(第六版) 学习笔记 ——《第七章 项目成本管理》
  13. Pycharm中光标变粗 光标进入改写状态
  14. 云计算——Google云计算原理与应用(Google文件系统GFS)
  15. 安规之电气间隙和爬电距离
  16. 2008年3月it公司红黑榜/口碑榜
  17. 《设计模式之禅》第二版 学习之六大设计原则(二)
  18. 《高效阅读-20分钟读懂一本书》笔记
  19. 6、文件的路径问题、错误的处理
  20. Theano简单入门(一):Theano与Lasagne的安装

热门文章

  1. 铁塔公司来了:运营商为何需要共建共享?
  2. 06.Google抓包技巧和方法
  3. win10系统盘多大合适_韩博士装机大师一键重装win10系统
  4. oracle 创建一揽子协议,SAP Business One 9.0 添加一揽子协议简介
  5. 转载:ELK filebeat原生处理日志时间
  6. (转载)初识小波变换——傅里叶变换的局限性
  7. CSS3-阴影nbsp;效果做成的立体图片效果
  8. 【游戏开发实战】可爱的动物数字,教你在Unity中自制UGUI艺术字体(位图字体)(Bitmap Font Generator)
  9. Vegas 使用教程(五)时间线
  10. 作团队感悟(15)----培养危机感