netty 系列之: 内置的 Frame detection
简介上篇文章我们讲到了 netty 中怎么自定义编码和解码器,但是自定义实现起来还是挺复杂的,一般没有特殊必要的情况下,大家都希望越简单越好,其难点就是找到 ByteBuf 中的分割点,将 ByteBuf 分割成为一个个的可以处理的单元。今天本文讲讲 netty 中自带的分割处理机制。
Frame detection 在上一章,我们提到了需要有一种手段来区分 ByteBuf 中不同的数据,也就是说找到 ByteBuf 中不同数据的分割点。如果首先将 ByteBuf 分割成一个个的独立的 ByteBuf,再对独立的 ByteBuf 进行处理就会简单很多。
netty 中提供了 4 个分割点的编码器,我们可以称之为 Frame detection,他们分别是 DelimiterBasedFrameDecoder, FixedLengthFrameDecoder, LengthFieldBasedFrameDecoder, 和 LineBasedFrameDecoder。
这几个类都是 ByteToMessageDecoder 的子类,接下来我们一一进行介绍。
DelimiterBasedFrameDecoder 首先是 DelimiterBasedFrameDecoder,看名字就知道这个是根据 delimiter 对 bytebuf 进行分割的解码器。什么是 delimiter 呢?
netty 中有一个 Delimiters 类,专门定义分割的字符,主要有两个 delimiter,分别是 nulDelimiter 和 lineDelimiter:
public static ByteBuf[] nulDelimiter() {return new ByteBuf[] {Unpooled.wrappedBuffer(new byte[] { 0 }) };}
nullDelimiter 用来处理 0x00,主要用来处理 Flash XML socket 或者其他的类似的协议。
lineDelimiter 用来处理回车和换行符,主要用来文本文件的处理中。
对于 DelimiterBasedFrameDecoder 来说,如果有多个 delimiter 的话,会选择将 ByteBuf 分割最短的那个,举个例子,如果我们使用 DelimiterBasedFrameDecoder(Delimiters.lineDelimiter()) ,因为 lineDelimiter 中实际上有两个分割方式,回车+换行或者换行,如果遇到下面的情况:
+--------------+| ABC\nDEF\r\n |+--------------+DelimiterBasedFrameDecoder 会选择最短的分割结果,也就说将上面的内容分割成为:
+-----+-----+| ABC | DEF |+-----+-----+而不是
+----------+| ABC\nDEF |+----------+FixedLengthFrameDecoder 这个类会将 ByteBuf 分成固定的长度,比如收到了下面的 4 块 byte 信息:
+---+----+------+----+| A | BC | DEFG | HI |+---+----+------+----+如果使用一个 FixedLengthFrameDecoder(3) ,则会将上面的 ByteBuf 分成下面的几个部分:
+-----+-----+-----+| ABC | DEF | GHI |+-----+-----+-----+LengthFieldBasedFrameDecoder 这个类就更加灵活一点,可以根据数据中的 length 字段取出后续的 byte 数组。LengthFieldBasedFrameDecoder 非常灵活,它有 4 个属性来控制他们分别是 lengthFieldOffset、lengthFieldLength、lengthAdjustment 和 initialBytesToStrip。
lengthFieldOffset 是长度字段的起始位置,lengthFieldLength 是长度字段本身的长度,lengthAdjustment 是对目标数据长度进行调整,initialBytesToStrip 是解密过程中需要删除的 byte 数目。理解不了?没关系,我们来举几个例子。
首先看一个最简单的:
lengthFieldOffset = 0lengthFieldLength = 2lengthAdjustment = 0initialBytesToStrip = 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 = 0lengthFieldLength = 2lengthAdjustment = 0initialBytesToStrip = 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 = 0lengthFieldLength = 2lengthAdjustment = -2 (= Length 字段的长度)initialBytesToStrip = 0
BEFORE DECODE (14 bytes) AFTER DECODE (14 bytes)+--------+----------------+ +--------+----------------+| Length | Actual Content |----->| Length | Actual Content || 0x000E | "HELLO, WORLD" | | 0x000E | "HELLO, WORLD" |+--------+----------------+ +--------+----------------+LineBasedFrameDecoderLineBasedFrameDecoder 专门处理文本文件中的一行结束。也就是 “\n” 和 “\r\n”,他和 DelimiterBasedFrameDecoder 很类似,但是 DelimiterBasedFrameDecoder 更加通用。
总结有了上面 4 个 Frame detection 装置之后,就可以在 pipline 中首先添加这些 Frame detection,然后再添加自定义的 handler,这样在自定义的 handler 中就不用考虑读取 ByteBuf 的长度问题了。
比如在 StringDecoder 中,如果已经使用了 LineBasedFrameDecoder , 那么在 decode 方法中可以假设传入的 ByteBuf 就是一行字符串,那么可以直接这样使用:
是不是很简单?
本文已收录于 http://www.flydean.com/15-netty-buildin-frame-detection/
最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧等你来发现!
欢迎关注我的公众号:「程序那些事」,懂技术,更懂你!
版权声明: 本文为 InfoQ 作者【程序那些事】的原创文章。
原文链接:【http://xie.infoq.cn/article/400ce591c22ebfbca9dc83325】。文章转载请联系作者。
评论