netty粘包处理、LengthFieldBasedFrameDecoder 解码器

最近项目中接触了netty。 因为是tcp连接。 在频繁接收消息的时候。可能会出现粘包问题。

粘包:发送端为了将多个发往接收端的包,更有效的发到对方,使用了优化方法(Nagle算法),将多次间隔较小且数据量小的数据,合并成一个大的数据块,然后进行封包。 接收端在接收的时候。会接收到2个报文粘在了一起。解析出现异常。这里就叫粘包

解决这个问题也比较简单。

netty提供了一个自定义长度的解码器。 只要配置一下 就可以完成解析啦。
要使用这个的前提是。你传输的数据中。 有一个字段是。长度域。根据这个长度域进行拆包。保证接收的数据是正常的。 当然。如果你的数据传输是使用 换行 或者其他自定义的分隔符。那可以使用netty中其他的解码器。

例如:

  • FixedLengthFrameDecoder:定长协议解码器,我们可以指定固定的字节数算一个完整的报文
  • LineBasedFrameDecoder: 行分隔符解码器,遇到\n或者\r\n,则认为是一个完整的报文
  • DelimiterBasedFrameDecoder: 分隔符解码器,与LineBasedFrameDecoder类似,只不过分隔符可以自己指定
  • LengthFieldBasedFrameDecoder:长度编码解码器,将报文划分为报文头/报文体,根据报文头中
  • Length字段确定报文体的长度,因此报文提的长度是可变的
  • JsonObjectDecoder:json格式解码器,当检测到匹配数量的"{" 、”}”或”[””]”时,则认为是一个完整的json对象或者json数组。

主角:LengthFieldBasedFrameDecoder

今天主要介绍一下这哥们,他共有7个参数。接下来会依次解释一下。

首先贴一下。 什么样的报文会用到这哥们

在这里插入图片描述
类似上述这种报文。就可以使用啦。 要有长度域哦

废话不多说,开始

首先贴一个netty构造函数的源码

public LengthFieldBasedFrameDecoder(ByteOrder byteOrder, int maxFrameLength, int lengthFieldOffset, int lengthFieldLength, int lengthAdjustment, int initialBytesToStrip, boolean failFast) {
    if (byteOrder == null) {
      throw new NullPointerException("byteOrder");
    } else if (maxFrameLength <= 0) {
      throw new IllegalArgumentException("maxFrameLength must be a positive integer: " + maxFrameLength);
    } else if (lengthFieldOffset < 0) {
      throw new IllegalArgumentException("lengthFieldOffset must be a non-negative integer: " + lengthFieldOffset);
    } else if (initialBytesToStrip < 0) {
      throw new IllegalArgumentException("initialBytesToStrip must be a non-negative integer: " + initialBytesToStrip);
    } else if (lengthFieldOffset > maxFrameLength - lengthFieldLength) {
      throw new IllegalArgumentException("maxFrameLength (" + maxFrameLength + ") must be equal to or greater than lengthFieldOffset (" + lengthFieldOffset + ") + lengthFieldLength (" + lengthFieldLength + ").");
    } else {
      this.byteOrder = byteOrder;
      this.maxFrameLength = maxFrameLength;
      this.lengthFieldOffset = lengthFieldOffset;
      this.lengthFieldLength = lengthFieldLength;
      this.lengthAdjustment = lengthAdjustment;
      this.lengthFieldEndOffset = lengthFieldOffset + lengthFieldLength;
      this.initialBytesToStrip = initialBytesToStrip;
      this.failFast = failFast;
    }
  }
  1. ByteOrder byteOrder:按照什么方式进行解码长度域的字段。大端模式 还是 小端模式;
  2. int maxFrameLength:数据最大的长度是多少;
  3. int lengthFieldOffset:长度域从第几个字节开始;
  4. int lengthFieldLength:长度域占了几个字节;
  5. int lengthAdjustment:长度域的偏移补偿;
  6. int initialBytesToStrip:跳过前面的几个字节;
  7. boolean failFast:如果为true,则表示读取到长度域,他的值的超过maxFrameLength,就抛出一个 TooLongFrameException,而为false表示只有当真正读取完长度域的值表示的字节之后,才会抛出 TooLongFrameException,默认情况下设置为true,建议不要修改,否则可能会造成内存溢出。

参数重点解释

int lengthAdjustment:长度域的偏移补偿;

这句话是啥意思。 很多读者看到这里。肯定要骂街了。 作者。请你说人话好吗。 啥叫长度域偏移补偿啊。 老子不明白啊。

请各位大哥稍安勿躁。只要记住下面的公式。啥报文来了都不怕好吗!

假设:

数据包长度(所有数据总长度):155
长度域的开始字节:2
长度域所占的长度字节:2
长度域里面存的值:155

lengthAdjustment = 数据包总长度(155) - 长度域开始字节(2) - 长度域所占字节(2)- 长度域里面存储的值(155)

所以这个参数就等于 -4

是不是很简单。

int initialBytesToStrip:跳过前面的几个字节;

一句话来概括这个参数的作用。 前面是不是有一些垃圾报文需要丢弃。 如果0 就是不丢弃。 如果2 就是丢弃 2个。是不是很简单。

实战一下

报文格式:

在这里插入图片描述


    @Override
    protected void initChannel(SocketChannel socketChannel) throws Exception {
        ChannelPipeline pipeline = socketChannel.pipeline();
        pipeline.addLast(new LengthFieldBasedFrameDecoder(
        				ByteOrder.LITTLE_ENDIAN,
         				1024,
				        2, 
				        2,
				        -4, 
			            0, 
		          		false));
    }

好啦。 各位老铁如果觉得有用。如果帮到你了。麻烦给兄弟点个赞。 感谢哦~

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值