我们接下来看一个也是比较重要的的解码器LengthFieldBasedFrameDecoder,这个和DelimiterBasedFrameDecoder比起来没有那么难理解,所以我们简单的看一下。
和之前一样,我们先来看一下局部变量。
- private final int maxFrameLength;
- private final int lengthFieldOffset;
- private final int lengthFieldLength;
- private final int lengthFieldEndOffset;
- private final int lengthAdjustment;
- private final int initialBytesToStrip;
- private final boolean failFast;
- private boolean discardingTooLongFrame;
- private long tooLongFrameLength;
- private long bytesToDiscard;
- maxFrameLength 这个定义最大帧的长度
- lengthFieldOffset 长度属性的起始指针(偏移量)
- lengthFieldLength 长度属性的长度,即存放数据包长度的变量的的字节所占的长度
- lengthFieldEndOffset 这个是一个快捷属性,是根据lengthFieldOffset和lengthFieldLength计算出来的,即就是起始偏移量+长度=结束偏移量
- lengthAdjustment 这个是一个长度调节值,例如当总长包含头部信息的时候,这个可以是个负数,就比较好实现了
- initialBytesToStrip 这个属性也比较好理解,就是解码后的数据包需要跳过的头部信息的字节数
- failFast 这个和DelimiterBasedFrameDecoder是一致的,就是如果设置成true,当发现解析的数据超过maxFrameLenght就立马报错,否则当整个帧的数据解析完后才报错
- discardingTooLongFrame 这个也是一个导出属性,就是当前编码器的状态,是不是处于丢弃超长帧的状态
- tooLongFrameLength 这个是当出现超长帧的时候,这个超长帧的长度
- bytesToDiscard 这个来定义,当出现超长帧的时候,丢弃的数据的字节数
接下来我们就深入主题,来看这个类的实现,最后我们再分析下javadoc,这样我们就能够彻底的掌握这个编码器了。
- if (discardingTooLongFrame) {
- long bytesToDiscard = this.bytesToDiscard;
- int localBytesToDiscard = (int) Math.min(bytesToDiscard, buffer.readableBytes());
- buffer.skipBytes(localBytesToDiscard);
- bytesToDiscard -= localBytesToDiscard;
- this.bytesToDiscard = bytesToDiscard;
- failIfNecessary(ctx, false);
- return null;
- }
- if (buffer.readableBytes() < lengthFieldEndOffset) {
- return null;
- }
这个逻辑是判断,如果当前的编码器处于丢弃超长帧的状态,这个状态肯定是上传编码的时候被设置成这个状态的,然后取上次丢弃的字节数和当前buffer里面的可读数据的最小值,后面的程序理解起来不是那么直接,这个bytesToDiscard属性主要是在后面设置的,bytesToDiscard = frameLength - buffer.readableBytes(); frameLength是当前超长帧的字节数,buffer.readableBytes是当前buffer里面的数据的字节数,所以它的含义就是告诉编码器下次还要丢弃的字节数,举个例子,最大帧的长度是1000,当前帧的长度是1024,所以超过了最大帧了,比如当前buffer里面长度是18个字节,所以bytesToDiscard就是1024-18 = 1006,含义就是说下次解码的时候还要丢弃1006个字节,那上面的代码就比较好理解了,就是说在计算下一次编码需要丢弃的字节数。后面是就是调用failIfNecessary函数了。
我们先来看一下它的实现吧:
- private void failIfNecessary(ChannelHandlerContext ctx, boolean firstDetectionOfTooLongFrame) {
- if (bytesToDiscard == 0) {
- // Reset to the initial state and tell the handlers that
- // the frame was too large.
- long tooLongFrameLength = this.tooLongFrameLength;
- this.tooLongFrameLength = 0;
- discardingTooLongFrame = false;
- if ((!failFast) ||
- (failFast && firstDetectionOfTooLongFrame))
- {
- fail(ctx, tooLongFrameLength);
- }
- } else {
- // Keep discarding and notify handlers if necessary.
- if (failFast && firstDetectionOfTooLongFrame)
- {
- fail(ctx, this.tooLongFrameLength);
- }
- }
- }
如果bytesToDiscard是0,就是说下次编码的时候不需要丢弃了,说明这个超长帧读取完毕,那么将这个编码器的状态设置为非丢弃超长帧状态,如果不是failFast状态或者是failFast又是第一次出现超长帧,就报错,意思其实挺明白,如果不是failFast状态,在这个超长帧读取完毕后理应抛出异常,如果是failFast是第一次发现了超长帧,所以也要抛出异常。else分支很好理解,就是说发现了超长帧,并且客户配置的是立马抛出异常,我们就直接抛出异常即可。
我们接着来看decode的代码实现:
- if (buffer.readableBytes() < lengthFieldEndOffset) {
- return null;
- }
- 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”);
- }
这段代码结构性很好,也很好理解,如果发现可读的字节数不够,就返回null,等待下次messageReceived通知,然后根据length的长度进行读操作。
我们接着超下看:
- if (frameLength < 0) {
- buffer.skipBytes(lengthFieldEndOffset);
- throw new CorruptedFrameException(
- ”negative pre-adjustment length field: ” + frameLength);
- }
- frameLength += lengthAdjustment + lengthFieldEndOffset;
- if (frameLength < lengthFieldEndOffset) {
- buffer.skipBytes(lengthFieldEndOffset);
- throw new CorruptedFrameException(
- ”Adjusted frame length (“ + frameLength + “) is less ” +
- ”than lengthFieldEndOffset: ” + lengthFieldEndOffset);
- }
- 如果我们发现读到的长度是负值,我们将read指针重新设置成读之前的位置,并抛出异常。
- 计算帧的长度,这个里面是lengthAdjustment + lengthFieldEndOffset,这个比较好理解,这个里面同样做了判断,就是当前帧的长度,不能比lenghtFieldEndOffset小,含义就是帧的数据内容可能为0,但是不可能为负
- if (frameLength > maxFrameLength) {
- // Enter the discard mode and discard everything received so far.
- discardingTooLongFrame = true;
- tooLongFrameLength = frameLength;
- bytesToDiscard = frameLength - buffer.readableBytes();
- buffer.skipBytes(buffer.readableBytes());
- failIfNecessary(ctx, true);
- return null;
- }
- // never overflows because it’s less than maxFrameLength
- int frameLengthInt = (int) frameLength;
- if (buffer.readableBytes() < frameLengthInt) {
- return null;
- }
这个代码就是在处理超长帧的问题,这个在上面我们已经进行解析了
- 如果发现当前帧的长度超过超长帧的定义,我们将当前的解码器标记为丢弃超长帧状态
- 计算下一次编码还需要丢弃的长度
- 修改buffer的读指针
- 调用failIfNecessary这个函数,判断是否需要抛出异常,关于failIfNecessary函数我们上面已经讲了
- 然后返回null标记此次解码失败,等待下次messageReceived通知
- 然后做一次判断,看当前可读数据是不是够一个int型的字节,因为现在马上要读取数据帧的长度了
- if (initialBytesToStrip > frameLengthInt) {
- buffer.skipBytes(frameLengthInt);
- throw new CorruptedFrameException(
- ”Adjusted frame length (“ + frameLength + “) is less ” +
- ”than initialBytesToStrip: ” + initialBytesToStrip);
- }
- buffer.skipBytes(initialBytesToStrip);
- // extract frame
- int readerIndex = buffer.readerIndex();
- int actualFrameLength = frameLengthInt - initialBytesToStrip;
- ChannelBuffer frame = extractFrame(buffer, readerIndex, actualFrameLength);
- buffer.readerIndex(readerIndex + actualFrameLength);
- return frame;
这个是decode的最后的实现了
- 最开始是做了一个判断,就是起始的跳过字节数的变量太大,也要抛出异常的
- 计算数据帧的长度,就是数据包头部里面定义的长度减去需要跳过的字节数
- 最后就是提取数据并修改读指针,最后返回数据帧
我们最后来看下这个类的javadoc吧,我感觉netty的作者作为和我们同年龄的80后青年,不但代码写的好,注释也写的很好,这种分享精神很值得我们学习。
- 第一种情况:从lenght从头开始,长度为2,不跳过头部
- lengthFieldOffset = 0
- lengthFieldLength = 2
- lengthAdjustment = 0
- initialBytesToStrip = 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” |
- +——–+—————-+ +——–+—————-+
- 第二种情况,从length从头开始,长度为2,跳过头部
- 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” |
- +——–+—————-+ +—————-+
这个和上面的区别是,数据经过解码后头部被去掉了
- 第三种情况,2个字节长度的头部,offset是0,但是长度代表的是整个数据帧的长度,即就是包含头部
- lengthFieldOffset = 0
- lengthFieldLength = 2
- lengthAdjustment = -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” |
- +——–+—————-+ +——–+—————-+
- 第四种情况,5个字节长度的头部,但是数据帧的lenght的字节数是3,offset是2,不跳过头部
- lengthFieldOffset = 2 (= the length of Header 1)
- lengthFieldLength = 3
- lengthAdjustment = 0
- initialBytesToStrip = 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个字节长度的头部,但是数据帧的lenght的字节数是3,offset是0,不跳过头部
- lengthFieldOffset = 0
- lengthFieldLength = 3
- lengthAdjustment = 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” |
- +———-+———-+—————-+ +———-+———-+—————-+
这个稍微解释一下,由于我们的头部是5个字节,但是length数据域只有3个字节,所以lengthAdjustment是2,表明需要2个字节来调整整个帧的长度。
- 第六种情况,4个字节长度的头部,但是数据帧的lenght的字节数是2,offset是1,跳过头部第一个数据域和长度域
- lengthFieldOffset = 1 (= the length of HDR1)
- lengthFieldLength = 2
- lengthAdjustment = 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” |
- +——+——–+——+—————-+ +——+—————-+
这个我稍微解释一下,HDR1是一个字节,Lenght是2个字节,HDR2是两个字节,解码后需要包含HDR2,所以lenghtFieldOffset是1,lenghtFieldLenght是2,lenghtAdjustment是1,实际上就是HDR2的长度,initailBytesToStrip是3,实际上就是HDR1+LEN的长度。
- 第七种情况,4个字节长度的头部,但是数据帧的lenght的字节数是2,offset是1,跳过头部第一个数据域和长度域,消息的长度代表整个数据帧的长度
- lengthFieldOffset = 1
- lengthFieldLength = 2
- lengthAdjustment = -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” |
这个我稍微解释一下,HDR1是一个字节,Lenght是2个字节,HDR2是两个字节,解码后需要包含HDR2,所以lenghtFieldOffset是1,lenghtFieldLenght是2,lenghtAdjustment是-3,实际上就是HDR1+LEN的长度,initailBytesToStrip是3,实际上就是HDR1+LEN的长度。
转自: http://asialee.iteye.com/blog/1784844
一些说明:
1.如果是自定义包结构进行数据传输,利用LengthFieldBasedFrameDecoder可以接收到完整数据包,但数据包中的各部分数据要取出来还需自己在messageReceived中处理 (基于netty3.x)
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e)
throws Exception {
ChannelBuffer buf = (ChannelBuffer) e.getMessage();
//根据自定义的包结构进行拆包处理
//假设前四字节是长度
int length = buf.readInt();
//假设后一字节是类型
byte type = buf.readByte();
//其它结构…
}
2.回写消息 (基于netty3.x)
byte [] returnMsg = “这是一个回写消息”.getBytes();
ChannelBuffer returnMsgBuffer = ChannelBuffers.buffer(returnMsg.length);
returnMsgBuffer.writeBytes(returnMsg);
ChannelFuture future = ctx.getChannel().write(returnMsgBuffer);//注意ctx 是 ChannelHandlerContext
future.addListener(new ChannelFutureListener(){
@Override
public void operationComplete(ChannelFuture future) throws Exception {
if(future.isSuccess()){
System.out.println(“回写消息成功”);
}else{
future.getChannel().close();
}
}
});
3.以上操作在netty4又有些不同,主要是部分类的名称发生了变化和方法发生了变化,但大致流程还是一样的
public void messageReceived(ChannelHandlerContext ctx, , Object msg)
throws Exception {
ByteBuf buf= (ByteBuf)msg;
//根据自定义的包结构进行拆包处理
//假设前四字节是长度
int length = buf.readInt();
//假设后一字节是类型
byte type = buf.readByte();
//其它结构…
}
回写消息
byte [] returnMsg = “这是一个回写消息”.getBytes();
ByteBuf returnMsgBuf = ctx.alloc().buffer(returnMsg.length);
returnMsgBuf.writeBytes(returnMsg);
ChannelFuture future = ctx.writeAndFlush(returnMsgBuf).sync();
sync()应该是把异步变成了同步
4.netty3.x中,如果不想使用LengthFieldBasedFrameDecoder,想自定义解码类来实现解码,则可以让自定义类继承FrameDecoder
我们接下来看一个也是比较重要的的解码器LengthFieldBasedFrameDecoder,这个和DelimiterBasedFrameDecoder比起来没有那么难理解,所以我们简单的看一下。
和之前一样,我们先来看一下局部变量。
- private final int maxFrameLength;
- private final int lengthFieldOffset;
- private final int lengthFieldLength;
- private final int lengthFieldEndOffset;
- private final int lengthAdjustment;
- private final int initialBytesToStrip;
- private final boolean failFast;
- private boolean discardingTooLongFrame;
- private long tooLongFrameLength;
- private long bytesToDiscard;
- maxFrameLength 这个定义最大帧的长度
- lengthFieldOffset 长度属性的起始指针(偏移量)
- lengthFieldLength 长度属性的长度,即存放数据包长度的变量的的字节所占的长度
- lengthFieldEndOffset 这个是一个快捷属性,是根据lengthFieldOffset和lengthFieldLength计算出来的,即就是起始偏移量+长度=结束偏移量
- lengthAdjustment 这个是一个长度调节值,例如当总长包含头部信息的时候,这个可以是个负数,就比较好实现了
- initialBytesToStrip 这个属性也比较好理解,就是解码后的数据包需要跳过的头部信息的字节数
- failFast 这个和DelimiterBasedFrameDecoder是一致的,就是如果设置成true,当发现解析的数据超过maxFrameLenght就立马报错,否则当整个帧的数据解析完后才报错
- discardingTooLongFrame 这个也是一个导出属性,就是当前编码器的状态,是不是处于丢弃超长帧的状态
- tooLongFrameLength 这个是当出现超长帧的时候,这个超长帧的长度
- bytesToDiscard 这个来定义,当出现超长帧的时候,丢弃的数据的字节数
接下来我们就深入主题,来看这个类的实现,最后我们再分析下javadoc,这样我们就能够彻底的掌握这个编码器了。
- if (discardingTooLongFrame) {
- long bytesToDiscard = this.bytesToDiscard;
- int localBytesToDiscard = (int) Math.min(bytesToDiscard, buffer.readableBytes());
- buffer.skipBytes(localBytesToDiscard);
- bytesToDiscard -= localBytesToDiscard;
- this.bytesToDiscard = bytesToDiscard;
- failIfNecessary(ctx, false);
- return null;
- }
- if (buffer.readableBytes() < lengthFieldEndOffset) {
- return null;
- }
这个逻辑是判断,如果当前的编码器处于丢弃超长帧的状态,这个状态肯定是上传编码的时候被设置成这个状态的,然后取上次丢弃的字节数和当前buffer里面的可读数据的最小值,后面的程序理解起来不是那么直接,这个bytesToDiscard属性主要是在后面设置的,bytesToDiscard = frameLength - buffer.readableBytes(); frameLength是当前超长帧的字节数,buffer.readableBytes是当前buffer里面的数据的字节数,所以它的含义就是告诉编码器下次还要丢弃的字节数,举个例子,最大帧的长度是1000,当前帧的长度是1024,所以超过了最大帧了,比如当前buffer里面长度是18个字节,所以bytesToDiscard就是1024-18 = 1006,含义就是说下次解码的时候还要丢弃1006个字节,那上面的代码就比较好理解了,就是说在计算下一次编码需要丢弃的字节数。后面是就是调用failIfNecessary函数了。
我们先来看一下它的实现吧:
- private void failIfNecessary(ChannelHandlerContext ctx, boolean firstDetectionOfTooLongFrame) {
- if (bytesToDiscard == 0) {
- // Reset to the initial state and tell the handlers that
- // the frame was too large.
- long tooLongFrameLength = this.tooLongFrameLength;
- this.tooLongFrameLength = 0;
- discardingTooLongFrame = false;
- if ((!failFast) ||
- (failFast && firstDetectionOfTooLongFrame))
- {
- fail(ctx, tooLongFrameLength);
- }
- } else {
- // Keep discarding and notify handlers if necessary.
- if (failFast && firstDetectionOfTooLongFrame)
- {
- fail(ctx, this.tooLongFrameLength);
- }
- }
- }
如果bytesToDiscard是0,就是说下次编码的时候不需要丢弃了,说明这个超长帧读取完毕,那么将这个编码器的状态设置为非丢弃超长帧状态,如果不是failFast状态或者是failFast又是第一次出现超长帧,就报错,意思其实挺明白,如果不是failFast状态,在这个超长帧读取完毕后理应抛出异常,如果是failFast是第一次发现了超长帧,所以也要抛出异常。else分支很好理解,就是说发现了超长帧,并且客户配置的是立马抛出异常,我们就直接抛出异常即可。
我们接着来看decode的代码实现:
- if (buffer.readableBytes() < lengthFieldEndOffset) {
- return null;
- }
- 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”);
- }
这段代码结构性很好,也很好理解,如果发现可读的字节数不够,就返回null,等待下次messageReceived通知,然后根据length的长度进行读操作。
我们接着超下看:
- if (frameLength < 0) {
- buffer.skipBytes(lengthFieldEndOffset);
- throw new CorruptedFrameException(
- ”negative pre-adjustment length field: ” + frameLength);
- }
- frameLength += lengthAdjustment + lengthFieldEndOffset;
- if (frameLength < lengthFieldEndOffset) {
- buffer.skipBytes(lengthFieldEndOffset);
- throw new CorruptedFrameException(
- ”Adjusted frame length (“ + frameLength + “) is less ” +
- ”than lengthFieldEndOffset: ” + lengthFieldEndOffset);
- }
- 如果我们发现读到的长度是负值,我们将read指针重新设置成读之前的位置,并抛出异常。
- 计算帧的长度,这个里面是lengthAdjustment + lengthFieldEndOffset,这个比较好理解,这个里面同样做了判断,就是当前帧的长度,不能比lenghtFieldEndOffset小,含义就是帧的数据内容可能为0,但是不可能为负
- if (frameLength > maxFrameLength) {
- // Enter the discard mode and discard everything received so far.
- discardingTooLongFrame = true;
- tooLongFrameLength = frameLength;
- bytesToDiscard = frameLength - buffer.readableBytes();
- buffer.skipBytes(buffer.readableBytes());
- failIfNecessary(ctx, true);
- return null;
- }
- // never overflows because it’s less than maxFrameLength
- int frameLengthInt = (int) frameLength;
- if (buffer.readableBytes() < frameLengthInt) {
- return null;
- }
这个代码就是在处理超长帧的问题,这个在上面我们已经进行解析了
- 如果发现当前帧的长度超过超长帧的定义,我们将当前的解码器标记为丢弃超长帧状态
- 计算下一次编码还需要丢弃的长度
- 修改buffer的读指针
- 调用failIfNecessary这个函数,判断是否需要抛出异常,关于failIfNecessary函数我们上面已经讲了
- 然后返回null标记此次解码失败,等待下次messageReceived通知
- 然后做一次判断,看当前可读数据是不是够一个int型的字节,因为现在马上要读取数据帧的长度了
- if (initialBytesToStrip > frameLengthInt) {
- buffer.skipBytes(frameLengthInt);
- throw new CorruptedFrameException(
- ”Adjusted frame length (“ + frameLength + “) is less ” +
- ”than initialBytesToStrip: ” + initialBytesToStrip);
- }
- buffer.skipBytes(initialBytesToStrip);
- // extract frame
- int readerIndex = buffer.readerIndex();
- int actualFrameLength = frameLengthInt - initialBytesToStrip;
- ChannelBuffer frame = extractFrame(buffer, readerIndex, actualFrameLength);
- buffer.readerIndex(readerIndex + actualFrameLength);
- return frame;
这个是decode的最后的实现了
- 最开始是做了一个判断,就是起始的跳过字节数的变量太大,也要抛出异常的
- 计算数据帧的长度,就是数据包头部里面定义的长度减去需要跳过的字节数
- 最后就是提取数据并修改读指针,最后返回数据帧
我们最后来看下这个类的javadoc吧,我感觉netty的作者作为和我们同年龄的80后青年,不但代码写的好,注释也写的很好,这种分享精神很值得我们学习。
- 第一种情况:从lenght从头开始,长度为2,不跳过头部
- lengthFieldOffset = 0
- lengthFieldLength = 2
- lengthAdjustment = 0
- initialBytesToStrip = 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” |
- +——–+—————-+ +——–+—————-+
- 第二种情况,从length从头开始,长度为2,跳过头部
- 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” |
- +——–+—————-+ +—————-+
这个和上面的区别是,数据经过解码后头部被去掉了
- 第三种情况,2个字节长度的头部,offset是0,但是长度代表的是整个数据帧的长度,即就是包含头部
- lengthFieldOffset = 0
- lengthFieldLength = 2
- lengthAdjustment = -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” |
- +——–+—————-+ +——–+—————-+