扫描下方二维码或者微信搜索公众号
菜鸟飞呀飞
,即可关注微信公众号,阅读更多Spring源码分析
和Java并发编程
文章。
前言
在上一篇文章中分析了三个比较简单的解码器,今天接着分析最后一个常用的解码器:LengthFieldBasedFrameDecoder,这是一个基于长度字段的解码器。什么意思呢?就是在发送的数据中,使用一个字段来表示数据的长度,这样当接收方接收到数据后,先读出这个长度字段,读到了长度字段,那就知道了这次发送的数据有多长,这样就能解码出数据了。
属性介绍
在 LengthFieldBasedFrameDecoder 中有几个重要的属性,其中大分部属性与上一篇文章中提到的解码器中的属性相同,只有 4 个属性是长度字段解码器特有的。如下所示。
// 长度字段的偏移量
private final int lengthFieldOffset;
// 长度字段的长度
private final int lengthFieldLength;
// 长度调整值
private final int lengthAdjustment;
// 跳过的初始字节数
private final int initialBytesToStrip;
// 长度字段结束的偏移量,是通过属性计算出来:lengthFieldOffset + lengthFieldLength
private final int lengthFieldEndOffset;
// ======= 下面属性与其他解码器中类似 =========
// 是否立即抛出失败信息,true表示立即
private final boolean failFast;
// 是否处于丢弃模式,true表示处于丢弃模式
private boolean discardingTooLongFrame;
// 需要丢弃的长度
private long tooLongFrameLength;
// 累计丢弃的字节数
private long bytesToDiscard;
既然说 LengthFieldBasedFrameDecoder 是基于长度字段的解码器,那么就肯定需要有字段告诉我长度在哪儿,也就是处于这一串数据的哪个位置?这个字段就是 lengthFieldOffset,它用来标识长度字段处于这一串数据中的哪一个地方。
另外,数据有长度,是用一个数字来存储的,这个数字也需要占用一定的字节数,因此 lengthFieldLength 就是用来标识这个数字占用多少字节的变量。
例如如下示例中,长度字段处于第 4 个位置,因此 lengthFieldOffset = 3。另外长度字段自身占用一个字节,且表示的值是:0x0A,用十进制表示就是 10,也就是消息体的长度是 10 个字节,从长度字段往后 10 个字节就是消息体的具体类容。
和行解码器和分隔符解码器类似,我们有时候希望解码出来的数据,不包含换行符或者分隔符,因此需要跳过一定的字节数后再解码数据。同样,在基于长度字段的解码器中,也会有这样类似的需求,因此 initialBytesToStrip 字段就是用来表示在解码时跳过多少个字节的数据。
通常我们发送的一串数据中,会包含消息头和消息体,在有些协议中喜欢直接使用长度字段来直接表示消息体的长度;而有些协议中,喜欢用长度字段来表示整个数据的长度,即:消息头的长度 + 消息体的长度,那么这个时候,数据的接收方在接收到消息后,想要知道消息体的内容,就无能为力了。怎么办呢?那就需要另外一个字段:lengthAdjustment。这个字段翻译过来就是长度的调整值,如果长度表示的是整个消息的长度,那么将 lengthAdjustment 设置为负数,用整个消息的长度加上这个负数,那就是消息体的长度了。
示例
在 LengthFieldBasedFrameDecoder 的源码中,官方给出了很多示例,下面结合官方给出的示例,来解释下上面 4 个重要属性的含义吧。
示例 1
当长度字段处于数据的第一个位置,后面是消息体。长度字段占用两个字节(0x000C 这是一个 16 进制数,表示的是数值是 12,占用两个字节)。解码之后的数据如下右边所示。
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" |
+--------+----------------+ +--------+----------------+
示例 2
当长度字段处于数据的第一个位置,后面是消息体。长度字段占用两个字节,由于 initialBytesToStrip = 2,因此在解码时需要跳过两个字节,所以解码之后的数据就不包含 0x000C 了,结果如下右边所示。
lengthFieldOffset = 0
lengthFieldLength = 2
lengthAdjustment = 0
initialBytesToStrip = 2
BEFORE DECODE (14 bytes) AFTER DECODE (12 bytes)
+--------+----------------+ +----------------+
| Length | Actual Content |----->| Actual Content |
|