写点什么

netty 系列之: 内置的 Frame detection

发布于: 18 小时前

简介上篇文章我们讲到了 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 }) };}


public static ByteBuf[] lineDelimiter() {    return new ByteBuf[] {            Unpooled.wrappedBuffer(new byte[] { '\r', '\n' }),            Unpooled.wrappedBuffer(new byte[] { '\n' }),    };}
复制代码


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 就是一行字符串,那么可以直接这样使用:


protected void decode(ChannelHandlerContext ctx, ByteBuf msg, List<Object> out) throws Exception {    out.add(msg.toString(charset));}
复制代码


是不是很简单?


本文已收录于 http://www.flydean.com/15-netty-buildin-frame-detection/


最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧等你来发现!


欢迎关注我的公众号:「程序那些事」,懂技术,更懂你!

发布于: 18 小时前阅读数: 4
用户头像

关注公众号:程序那些事,更多精彩等着你! 2020.06.07 加入

最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧,尽在公众号:程序那些事!

评论

发布
暂无评论
netty系列之:内置的Frame detection