使用LengthFieldBasedFrameDecoder解决复杂的自定义协议-粘包与半包问题

之前做过一个项目,项目中web应用为了与传感器通讯,定义了一整套通讯协议,这里拿最简单的心跳协议来讲,使用netty自带的LengthFieldBasedFrameDecoder解码器来解决粘包与半包问题。
心跳协议如下:
这里写图片描述

简单说下这个协议,固定值的包头包尾设定,更多的是为了迎合硬件,如果你去看过一下rpc框架的通信协议,比如dubbo,为了使包字节数更少,不会这样。所以站在高效通讯的角度上讲,这个协议设计并不合理。

回到主题,上面这套协议算是LengthFieldBasedFrameDecoder最复杂的一种应用吧。
先看看LengthFieldBasedFrameDecoder的一个构造函数源码,如下:

public LengthFieldBasedFrameDecoder(
        int maxFrameLength,
        int lengthFieldOffset, int lengthFieldLength,
        int lengthAdjustment, int initialBytesToStrip) {
    this(
            maxFrameLength,
            lengthFieldOffset, lengthFieldLength, lengthAdjustment,
            initialBytesToStrip, true);
}

入参有五个参数,分别解释如下:

  • maxFrameLength:单个包最大的长度,这个值根据实际场景而定,我设置的是1024,固然我的心跳包不大,但是其他包可能比较大。
  • lengthFieldOffset:表示数据长度字段开始的偏移量,比如上面的协议,lengthFieldOffset应该取值为5,因为数据长度之前有2个字节的包头,1个字节的功能ID,2个字节的设备ID,一共为5。
  • lengthFieldLength:数据长度字段的所占的直接数,上面的协议中写的是2个字节,所以取值为2
  • lengthAdjustment:这里取值为10=7(系统时间) + 1(校验码)+ 2 (包尾),如果这个值取值为0,试想一下,解码器跟数据长度字段的取值(这里数据长度内容肯定是1),只向后取一个字节,肯定不对。
    (lengthAdjustment + 数据长度取值 = 数据长度字段之后剩下包的字节数)
  • initialBytesToStrip:表示从整个包第一个字节开始,向后忽略的字节数,我设置为0,本来可以忽略掉包头的两个字节(即设置为2),但是,实际项目中,需要校验包头取值是否为AA55,来判断包的合法性。
@Override
protected void initChannel(SocketChannel ch) throws Exception {
    ch.pipeline().addLast(new LengthFieldBasedFrameDecoder(1024,5,2,10,0));
//  ch.pipeline().addLast(new AuthCheckHandler());
}

所以LengthFieldBasedFrameDecoder设置如上面代码所示。

猜你喜欢

转载自blog.csdn.net/u014801432/article/details/81909902