[toc]上一篇文章我们讲了如何在netty中自定义encoder和decoder,但是自定义实现还是比较复杂的。一般如果没有特殊需要,大家都希望越简单越好,难点在于在ByteBuf中找到切分点,将ByteBuf分成一个个可以处理的单元。今天这篇文章讲一下netty自带的切分处理机制。帧检测在上一章中,我们提到需要有一种手段来区分ByteBuf中不同的数据,即找到ByteBuf中不同数据的切分点。如果先把ByteBuf分成一个个的ByteBuf,那么处理一个个的ByteBuf会容易很多。Netty提供了4种分割点编码器,我们可以称之为帧检测,它们是DelimiterBasedFrameDecoder、FixedLengthFrameDecoder、LengthFieldBasedFrameDecoder和LineBasedFrameDecoder。这些类都是ByteToMessageDecoder的子类,我们会一一介绍。DelimiterBasedFrameDecoder首先是DelimiterBasedFrameDecoder。看名字就知道这是一个根据分隔符划分bytebuf的解码器。什么是定界符?netty中有一个Delimiters类,专门定义了分割字符。有两个主要的分隔符,即nulDelimiter和lineDelimiter:publicstaticByteBuf[]nulDelimiter(){returnnewByteBuf[]{Unpooled.wrappedBuffer(newbyte[]{0})};}publicstaticByteBuf[]lineDelimiter(){returnnewByteBuf[]{Unpooled.wrappedBuffer(newbyte[]{'\r','\n'}),Unpooled.wrappedBuffer(newbyte[]{'\n'}),};}nullDelimiter是用来处理0x00的,主要用来处理FlashXMLsocket或者其他类似的协议。lineDelimiter用于处理回车和换行,主要用于文本文件的处理。对于DelimiterBasedFrameDecoder,如果有多个分隔符,将选择将ByteBuf分成最短的分隔符。比如我们使用DelimiterBasedFrameDecoder(Delimiters.lineDelimiter()),因为lineDelimiter中其实有两种切分方式,回车+换行或者换行,如果遇到如下情况:+---------------+|ABC\nDEF\r\n|+--------------+DelimiterBasedFrameDecoder会选择最短的分割结果,也就是把上面的内容分成:+-----+-----+|美国广播公司|防御力|+-----+-----+而不是+---------+|ABC\n防御|+------------+FixedLengthFrameDecoder这个类会把ByteBuf分成固定长度,比如接收到如下4个Block字节信息:+---+----+------+----+|一个|公元前|定义|嗨|+---+----+------+----+如果使用了FixedLengthFrameDecoder(3),上面的ByteBuf会被分成以下几个部分:+-----+-----+-----+|美国广播公司|防御力|GHI|+-----+-----+-----+LengthFieldBasedFrameDecoder这个类比较灵活,可以根据数据中的长度字段取出后面的字节数组。LengthFieldBasedFrameDecoder非常灵活,它有4个属性来控制它们,分别是lengthFieldOffset、lengthFieldLength、lengthAdjustment和initialBytesToStrip。lengthFieldOffset是长度字段的起始位置,lengthFieldLength是长度字段本身的长度,lengthAdjustment是调整目标数据的长度,initialBytesToStrip是解密过程中要删除的字节数。看不懂?没关系,举几个例子吧。先看最简单的:lengthFieldOffset=0lengthFieldLength=2lengthAdjustment=0initialBytesToStrip=0BEFOREDECODE(14bytes)A??FTERDECODE(14bytes)+------+----------------++--------+----------------+|长度|实际内容|----->|实际内容||0x000C|“你好,世界”||0x000C|“你好,世界”|+--------+----------------++-------+--------------+上面的设置表示长度从第0位开始,长度为2个字节。其中,Ox00C=12,也是“HELLO,WORLD”的长度。如果不需要Length字段,可以通过设置initialBytesToStrip删除长度:lengthFieldOffset=0lengthFieldLength=2lengthAdjustment=0initialBytesToStrip=2(=length字段长度)BEFOREDECODE(14bytes)A??FTERDECODE(12bytes)+---------+----------------++----------------+|长度|实际内容|----->|实际内容||0x000C|“你好,世界”||“你好,世界”|+--------+----------------++--------------+lengthAdjustment是调整数值Length域的,因为在某些情况下Length域可能包含了整个数据的长度,即Length+content,所以在解析的时候需要Adjust,比如下面的例子,真实的长度其实是0x0C,但是输入是0x0E,所以Length字段的长度需要减2,即lengthAdjustment设置为-2。lengthFieldOffset=0lengthFieldLength=2lengthAdjustment=-2(=长度字段的长度)initialBytesToStrip=0解码前(14字节)解码后(14字节)+-------+---------------++--------+----------------+|长度|实际内容|----->||实际内容||0x000E|“你好,世界”||0x000E|------+----------------+LineBasedFrameDecoderLineBasedFrameDecoder专门处理文本文件中的行尾。也就是“\n”和“\r\n”,他和DelimiterBasedFrameDecoder很像,但是DelimiterBasedFrameDecoder更通用。总结有了以上四种Frame检测设备后,可以先在pipline中添加这些Frame检测,然后再添加自定义handler,这样就不用考虑自定义handler中读取的ByteBuf的长度了。比如在StringDecoder中,如果已经使用了LineBasedFrameDecoder,那么在decode方法中,可以假设传入的ByteBuf是一行字符串,那么就可以直接这样使用:protectedvoiddecode(ChannelHandlerContextctx,ByteBufmsg,List
