乐趣区

关于netty:netty系列之内置的Frame-detection

[toc]

简介

上篇文章咱们讲到了 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   = 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" |
   +--------+----------------+      +--------+----------------+

下面的设置示意,length 是从第 0 位开始的,长度是 2 个字节。其中 Ox00C=12, 这也是“HELLO, WORLD”的长度。

如果不想要 Length 字段,能够通过设置 initialBytesToStrip 把 length 删除:

   lengthFieldOffset   = 0
   lengthFieldLength   = 2
   lengthAdjustment    = 0
   initialBytesToStrip = 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   =  0
   lengthFieldLength   =  2
   lengthAdjustment    = -2 (= Length 字段的长度)
   initialBytesToStrip =  0

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

LineBasedFrameDecoder

LineBasedFrameDecoder 专门解决文本文件中的一行完结。也就是 “\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/

最艰深的解读,最粗浅的干货,最简洁的教程,泛滥你不晓得的小技巧等你来发现!

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

退出移动版