欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2024-02-19 23:45:10
...

之前做过一个项目,项目中web应用为了与传感器通讯,定义了一整套通讯协议,这里拿最简单的心跳协议来讲,使用netty自带的LengthFieldBasedFrameDecoder解码器来解决粘包与半包问题。
心跳协议如下:
使用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设置如上面代码所示。

相关标签: netty