[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/
最艰深的解读,最粗浅的干货,最简洁的教程,泛滥你不晓得的小技巧等你来发现!
欢送关注我的公众号:「程序那些事」,懂技术,更懂你!
发表回复