从零开始学netty——如何面对粘包和拆包

本篇只有部分代码,重复代码见
从零开始学netty——第一个netty程序
从零开始学netty——认识decoder


粘包和拆包

tcp是流式套接字,这个就是造成了收到的内容和传输的的断句是不同的。这个可以类比古代没有标点,断句就可能有多种的变化。例如“没有鸡鸭也可以”这句。你可能收到是的没有鸡鸭也可以,也可能收到的是没有鸡,然后又收到鸭也可以。粘包说的是两次发送的一次收到了,拆包则是一次发送的,分两次收到。


产生的原因

原因大概说一下,详细了解的话,去专门看看tcp协议。

  1. write写入的字节大于套接字发送缓冲区的大小。
  2. 进行mss大小的tcp分段。
  3. 以太网帧的payload大于mtu进行ip分片。


解决方法

  1. 固定长度,例如每次只发200个字节,也只读200个字节,度不够就等待
  2. 使用分隔符,例如回车换行
  3. 用消息头和消息体


不知不觉使用的处理方式

其实很多人说自己压根没有专门做过粘包和拆包的处理,程序也没问题。我们仔细回想一下,如果自己写个socket的demo的时候,是不是还用的是包装流的做法,例如使用了BufferedReader,并且使用了readLine方法,每次取一行。这里就是上面所说的使用了分隔符的解决方法。


解决方式的对比

  1. 固定长度的方法麻烦的是长度固定,不够要补充空,浪费资源,而且不够的话,还得修改长度,扩展不好。
  2. 分隔符的方式比较简单,就是怕输入的内容也有分割符。
  3. 自定义消息这种是比较常用的,麻烦的是需要自己定义消息规则,在处理简单的情况的时候,上面两种完全可以够用。


demo演示

        ch.pipeline().addLast(new LineBasedFrameDecoder(2000));

LineBasedFrameDecoder就是类比我们BufferedReader的readLine的功能。加上这个解码器,你就发现telnet send发出收不到消息了,需要发送一个回车换行符号才可以。

                            ch.pipeline().addLast(
                                    new DelimiterBasedFrameDecoder(10000, Unpooled.copiedBuffer("$".getBytes())));

DelimiterBasedFrameDecoder是自定义分隔符的。这里我们使用$来作为分隔符。使用telnet发送hhh$,就会收到hhh

            ch.pipeline().addLast(new FixedLengthFrameDecoder(2));

FixedLengthFrameDecoder是定长的解码器,这里设置2的长度,你使用telnet发送bbbbbb,会分开接收为3个bb。

用消息头和消息体的情况和编码器和解码器的的小结一起写。


小结

netty提供了很常用的几种处理粘包和拆包的方式。在简单的逻辑下,他们是可以满足要求的,可以不用重复造轮子。

点赞