最近项目中接触了netty。 因为是tcp连接。 在频繁接收消息的时候。可能会出现粘包问题。
粘包:发送端为了将多个发往接收端的包,更有效的发到对方,使用了优化方法(Nagle算法),将多次间隔较小且数据量小的数据,合并成一个大的数据块,然后进行封包。 接收端在接收的时候。会接收到2个报文粘在了一起。解析出现异常。这里就叫粘包
解决这个问题也比较简单。
netty提供了一个自定义长度的解码器。 只要配置一下 就可以完成解析啦。 要使用这个的前提是。你传输的数据中。 有一个字段是。长度域。根据这个长度域进行拆包。保证接收的数据是正常的。 当然。如果你的数据传输是使用 换行 或者其他自定义的分隔符。那可以使用netty中其他的解码器。
例如:
FixedLengthFrameDecoder:定长协议解码器,我们可以指定固定的字节数算一个完整的报文LineBasedFrameDecoder: 行分隔符解码器,遇到\n或者\r\n,则认为是一个完整的报文DelimiterBasedFrameDecoder: 分隔符解码器,与LineBasedFrameDecoder类似,只不过分隔符可以自己指定LengthFieldBasedFrameDecoder:长度编码解码器,将报文划分为报文头/报文体,根据报文头中Length字段确定报文体的长度,因此报文提的长度是可变的JsonObjectDecoder:json格式解码器,当检测到匹配数量的"{" 、”}”或”[””]”时,则认为是一个完整的json对象或者json数组。今天主要介绍一下这哥们,他共有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; } } ByteOrder byteOrder:按照什么方式进行解码长度域的字段。大端模式 还是 小端模式;int maxFrameLength:数据最大的长度是多少;int lengthFieldOffset:长度域从第几个字节开始;int lengthFieldLength:长度域占了几个字节;int lengthAdjustment:长度域的偏移补偿;int initialBytesToStrip:跳过前面的几个字节;boolean failFast:如果为true,则表示读取到长度域,他的值的超过maxFrameLength,就抛出一个 TooLongFrameException,而为false表示只有当真正读取完长度域的值表示的字节之后,才会抛出 TooLongFrameException,默认情况下设置为true,建议不要修改,否则可能会造成内存溢出。这句话是啥意思。 很多读者看到这里。肯定要骂街了。 作者。请你说人话好吗。 啥叫长度域偏移补偿啊。 老子不明白啊。
请各位大哥稍安勿躁。只要记住下面的公式。啥报文来了都不怕好吗!
假设:
数据包长度(所有数据总长度):155 长度域的开始字节:2 长度域所占的长度字节:2 长度域里面存的值:155
lengthAdjustment = 数据包总长度(155) - 长度域开始字节(2) - 长度域所占字节(2)- 长度域里面存储的值(155)
所以这个参数就等于 -4
是不是很简单。
一句话来概括这个参数的作用。 前面是不是有一些垃圾报文需要丢弃。 如果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)); }好啦。 各位老铁如果觉得有用。如果帮到你了。麻烦给兄弟点个赞。 感谢哦~