Netty4系列 LengthFieldBasedFrameDecoder的用法

文章目录

    • 前言
    • LengthFieldBasedFrameDecoder的用法
        • 1. 基于长度的拆包
        • 2. 基于长度的截断拆包
        • 3. 基于偏移长度的拆包
        • 4. 基于可调整长度的拆包
        • 5. 基于偏移可调整长度的截断拆包
        • 6. 基于偏移可调整变异长度的截断拆包

前言

对Netty总有一种莫名地执着,虽然还是一知半解,当做一种慢慢积累的过程。


LengthFieldBasedFrameDecoder的用法

我们来看一下Netty通用的拆包器LengthFieldBasedFrameDecoder相关的使用,这个拆包器十分强大,几乎所有和长度相关的二进制协议都可以通过它来进行拆包。(以下图来自Netty源码注释)

1. 基于长度的拆包

Netty4系列 LengthFieldBasedFrameDecoder的用法_第1张图片
这类的数据包协议是比较常见的,前面的几个字节表示数据包的长度(不包括长度域),后面是具体的数据。进行拆包之后,数据包就是一个完整的带有长度域的数据包。

整个数据包占据14bytes,而长度域0x000C为12(该字段表示是消息体正文的长度),那么长度域字段占据2个字节。

LengthFieldBasedFrameDecoder decoder = new LengthFieldBasedFrameDecoder(
                Integer.MAX_VALUE, 0, 2);

使用以上的参数即可实现此类协议的拆包。

参数说明:

  1. maxFrameLength:数据包的最大长度。
  2. lengthFieldOffset:长度域的偏移量,这里由于长度域在包体的最前面,所以无偏移,lengthFieldOffset为0。
  3. lengthFieldLength:长度域的长度,这里长度域字段占据2个字节。

2. 基于长度的截断拆包

Netty4系列 LengthFieldBasedFrameDecoder的用法_第2张图片
如果我们的解码器不需要利用到长度域字段,拆完包后整个数据包就只有消息体正文。我们只需要指定initialBytesToStrip参数即可。

LengthFieldBasedFrameDecoder decoder = new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE,
                0, 2, 0, 2);

前三个参数和前面是一样的,而第四个参数后面再利用,这不多做讲解。我们只需要指定initialBytesToStrip参数即可。这里长度域字段占据2个字节,那么initialBytesToStrip的值就为2,表示我们进行拆包的时候需要跳过2个字节。

3. 基于偏移长度的拆包

在这里插入图片描述
这类二进制协议更加常见,前面几个固定字节表示的是协议头,紧接着表示的是一个长度域(这里也是表示消息体正文的字节数),最后才是消息体的正文。
由于长度域字段之前有个Header1,所以我们进行拆包时需要利用到lengthFieldOffset,这里lengthFieldOffset为3。

LengthFieldBasedFrameDecoder decoder = new LengthFieldBasedFrameDecoder(
							Integer.MAX_VALUE, 3, 2);

4. 基于可调整长度的拆包

如果长度域在前,协议头在长度域之后,如何拆包?
在这里插入图片描述
这时候我们就需要利用到参数lengthAdjustment了。
我们来分析一下参数设置的步骤:

  1. 长度域在最前面,无偏移,长度域占据两个字节。则lengthFieldOffset为0,lengthFieldLength为2。
  2. 长度域表示的是消息体正文的长度,忽略了Header 1的长度,如果我们按之前的方法进行拆包的话,就会得到一个不完整的包。这里我们就需要利用lengthAdjustment,这个参数是用来调整包体的大小的,这里长度域的数值加上lengthAdjustment这个调整值之后,就表示带有header的包。这里的Header 1的长度为3,那么lengthAdjustment的值就为3,所以header和消息体正文一种占据15(12+3)个字节。
LengthFieldBasedFrameDecoder decoder = new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0,
                2, 3, 0);

5. 基于偏移可调整长度的截断拆包

在这里插入图片描述
这类的二进制协议会出现两个协议头。我们需要实现拆包后,只留下HDR2和消息体正文。这类协议包在RPC中比较常见,一般HDR1可以表示magicNumber,表示应用只接受以该magicNumber开头的二进制数据。

我们来分析一下:

  1. 长度域不在数据包最前面,HDR1占据一个字节,Length占据两个字节,那么lengthFieldOffset为1,lengthFieldLength为2。
  2. 长度域表示的是消息体正文的长度,但是拆包时HDR2也会被当成包体的一部分来进行拆包,HDR2占据1个字节,那么lengthAdjustment为1。
  3. 由于我们只需要得到一个HDR2和消息体正文的包,所以我们需要跳过HDR1和Length,所以initialBytesToStrip的值就为3。
LengthFieldBasedFrameDecoder decoder = new LengthFieldBasedFrameDecoder(
                Integer.MAX_VALUE, 1, 2, 1, 3);

6. 基于偏移可调整变异长度的截断拆包

之前的长度域都是只表示了消息体正文的长度,那么要是长度域表示的是整个数据包的大小,应该如何处理。
在这里插入图片描述
这里注意一下Length的参数,0x0010为16,表示的是整个数据包的大小,和情况5的图区分一下。

其他参数都不需要多作解释,我们只需要注意参数lengthAdjustment即可,由于这次长度域表示的是整个数据包的长度,所以拆包时肯定会包含HDR2和消息体正文,可是会数据包会多出来一部分,这部分就是HDR1和Length的长度和,多出来的数据是不属于当前包的,那么就利用lengthAdjustment对其进行调整,此时lengthAdjustment为-3。那么包含HDR2和消息体正文的包体一共占据13个(16-3)字节。

LengthFieldBasedFrameDecoder decoder = new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE,
                1,2, -3, 3);

至此, LengthFieldBasedFrameDecoder的用法已经全部讲解完。LengthFieldBasedFrameDecoder拆包器的功能是十分强大的,足以应付大多数的二进制协议。

至于Netty源码暂时不去分析,这个以后再说。

参考:
netty源码分析之LengthFieldBasedFrameDecoder

你可能感兴趣的:(Netty系列)