LengthFieldBasedFrameDecoder学习

我之前学习Netty的课程时出现了LengthFieldBasedFrameDecoder解码器,当时我只想了解学习这门技术,所以很多技术点我都没有深入研究,现在工作有这方面的需要了,我需要开始深入研究一下。

LengthFieldBasedFrameDecoder解码器主要用于自定义协议,通常协议的格式为:

header | length | body

构造函数

其构造函数如下:

1
2
3
4
5
6
7
8

public LengthFieldBasedFrameDecoder(
            int maxFrameLength,
            int lengthFieldOffset, 
            int lengthFieldLength,
            int lengthAdjustment, 
            int initialBytesToStrip);

参数含义分别如下:

  • maxFrameLength:最大帧长度。也就是可以接收的数据的最大长度。如果超过,此次数据会被丢弃

  • lengthFieldOffset:长度域偏移。就是说数据开始的几个字节可能不是表示数据长度,需要后移几个字节才是长度域

  • lengthFieldLength:长度域字节数。用几个字节来表示数据长度

  • lengthAdjustment:数据长度修正。因为长度域指定的长度可以是header + body的整个长度,也可以只是body的长度。如果表示header + body的整个长度,那么我们需要修正数据长度

  • initialBytesToStrip:跳过的字节数。如果你需要接收header+body的所有数据,此值就是0,如果你只想接收body数据,那么需要跳过header所占用的字节数。

原课程中的应用

如下代码为我对该类的应用

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18

public class MsgSpliter extends LengthFieldBasedFrameDecoder {
    public MsgSpliter() {
        super(Integer.MAX_VALUE, 
                    SystemConstant.LENGTH_FIELD_OFFSET, 
                    SystemConstant.LENGTH_FIELD_LENGTH);
    }

    @Override
    protected Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception {
        if (in.getInt(in.readerIndex()) != SystemConstant.MAGIC_NUMBER) {
            ctx.channel().close();
            return null;
        }
        return super.decode(ctx, in);
    }
}

我的代码源于之前学习的课程,我的方案中我使用了一个叫做魔数的东西,如果用户传递的消息的魔数和我定义的不符合,则我会关闭消息通道。

在我的应用中,我的消息的长度可以是任意的,长度从第7个字节开始,长度本身占用四个字节。至于lengthAdjustment,由于我的length记录的就是我body的长度,所以我传递为零;因为我需要接受所有的数据,所以,initialBytesToStrip同样为零。

如果不使用该解码器该如何实现

我会在read中,判断当前可读的字节数是否大于4 + 4,如果大于,则我会先读取7个字节作为魔数,然后再读4个字节作为长度。

在实现的过程中,我可能会将对消息的解码放在同一个解码器中实现,所以我下一步可能会持续判断可读字节数是否为消息中定义的长度,如果是,则读取该长度的字节数,转码为package对象,然然后从package对象中获得字节码数据,转成对应的消息。在该实现过程中,我进行调用了package对象进行了两次转换。

如果需要进行优化,我会选择将package对象给刨除掉,我直接在,我的消息格式大概如下:

魔数 | 协议编号 | 消息体长度 | 消息内容

(实际上协议编号和消息体长度的先后顺序没有任何影响,所以没有必要纠结其前后)

我暂时不打算考虑协议版本号、编解码算法等问题,我直接默认用protobuf。