龙空技术网

细说 Netty 中的粘包和拆包

菜根谭 145

前言:

此时你们对“netty的粘包与拆包”大约比较看重,咱们都需要知道一些“netty的粘包与拆包”的相关知识。那么小编在网摘上搜集了一些有关“netty的粘包与拆包””的相关文章,希望朋友们能喜欢,咱们快快来了解一下吧!

TCP/IP中的“粘包”与“拆包”

“粘包拆包”是个伪命题

确实,我也认为这是个伪命题,tcp这种双工面向流的协议,本来就没有粘拆包的说法,包的界限问题应该需要由上层的应用处理。

但为什么会有粘拆包问题呢?

应用程序写入的数据大于套接字缓冲区大小,这将会发生拆包。应用程序写入数据小于套接字缓冲区大小,网卡将应用多次写入的数据发送到网络上,这将会发生粘包。进行MSS(最大报文长度)大小的TCP分段,当TCP报文长度-TCP头部长度>MSS的时候将发生拆包。接收方法不及时读取套接字缓冲区数据,这将发生粘包。(例如连接复用时,如不处理包界限问题一定会发生“粘包”,因为tcp并不知道接收的数据属于应用的第几次报文)

在应用层角度来观察粘拆包

写一个简易版TCP Server

//接收4k*1000大小的数据 ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); serverSocketChannel.bind(new InetSocketAddress(9527)); SocketChannel socketChannel = serverSocketChannel.accept(); int size = 4096*1000; ByteBuffer byteBuffer = ByteBuffer.allocate(size); while (byteBuffer.hasRemaining()){ int read = socketChannel.read(byteBuffer); System.out.println(read); }

简易版TCP Client

//发送4k*1000大小的数据 int size = 4096*1000; ByteBuffer byteBuffer = ByteBuffer.allocate(size); SocketChannel socketChannel = SocketChannel.open(); socketChannel.connect(new InetSocketAddress("127.0.0.1",9527)); for (int i = 0; i < size; i++) { byteBuffer.put((byte) 1); } byteBuffer.flip(); while (byteBuffer.hasRemaining()){ int write = socketChannel.write(byteBuffer); System.out.println(write); }

当前环境下,MSS是1380,IP MTU=MSS+20bytes(IP包头)+20bytes(TCP包头),IP层分片默认是禁用的(Don`t fragment)

看一下执行结果:

//服务端打印结果3967227362736273613680273619152273610944273654722736164162736273612312136854721368

从日志上看,每次读取的报文最小值是1368,刚好比mss小一点点(mss只是最大报文段长度,实际可读取的值需要减去各层协议的首部大小,所以最小值是1368)。每次读取的长度值有波动,但都是1368的整数倍。

由此可见,每次可读取的报文大小,都是以ip数据报为单位的。接收端每次接收的报文大小也都是以ip数据报为单位。

所以在读取报文(tcp buffer)时,也是以ip数据报为单位,绝对不会出现读取到半个ip包的问题(忽略因mtu大小导致的ip层分片)。

那么粘包拆包里的这个“包”的最小单位也是一个IP数据报

Netty中的粘拆包处理

Netty中并没有直接说粘包拆包这个问题,但《Netty权威指南》这本书上倒解释了粘包拆包,不用纠结这个名词,跟着大多数人叫也没错,错的人多了也就是对的。

Netty的请求处理是一个Pipeline结构,通过handler接口,可以定义不同的encoder/decoder,从而解决粘包拆包(处理包界限)问题,当然也可以自己处理,原理都是相同的。

Netty中内置了几个编解码器,可以很简单的处理包界限问题。

LengthFieldBasedFrameDecoder

通过在包头增加消息体长度的解码器,解析数据时首先获取首部长度,然后定长读取socket中的数据。

LineBasedFrameDecoder

换行符解码器,报文尾部增加固定换行符rn,解析数据时以换行符作为报文结尾。

DelimiterBasedFrameDecoder

分隔符解码器,使用特定分隔符作为报文的结尾,解析数据时以定义的分隔符作为报文结尾

FixedLengthFrameDecover

定长解码器,这个最简单,消息体固定长度,解析数据时按长度读取即可

标签: #netty的粘包与拆包 #netty粘包拆包处理 #程序拆包是什么意思