“粘包拆包”是个伪命题
确实,我也认为这是个伪命题,tcp这种双工面向流的协议,原本就没有粘拆包的说法,包的界限问题应该须要由上层的应用处理。网络
发送端的字节流都会先传入缓冲区,再经过网络传入到接收端的缓冲区中,最终由接收端获取。socket
写一个简易版TCP Servertcp
//接收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 Clientspa
//发送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层分片默认是禁用的(Don`t fragment),忽略便可日志
看一下执行结果:code
//服务端打印结果 39672 2736 2736 2736 13680 2736 19152 2736 10944 2736 5472 2736 16416 2736 2736 12312 1368 5472 1368
从日志上看,每次读取的报文最小值是1368,恰好比mss小一点点(mss只是最大报文段长度,实际可读取的值须要减去各层协议的首部大小,因此最小值是1368)。每次读取的长度值虽然有波动,但都是1368的整数倍。server
因而可知,每次可读取的报文大小(tcp buffer中),都是以ip数据报为单位的。接收端每次接收的报文大小也都是以ip数据报为单位。blog
因此在读取报文(tcp buffer)时,也是以ip数据报为单位,绝对不会出现读取到半个ip包的问题(忽略因mtu大小致使的ip层分片)。接口
那么粘包拆包里的这个“包”的最小单位也是一个IP数据报ip
Netty中并无直接说粘包拆包这个问题,但《Netty权威指南》这本书上倒解释了粘包拆包,不用纠结这个名词,跟着大多数人叫也没错,错的人多了也就是对的。
Netty的请求处理是一个Pipeline结构,经过handler接口,能够定义不一样的encoder/decoder,从而解决粘包拆包(处理包界限)问题,固然也能够本身处理,原理都是相同的。
Netty中内置了几个编解码器,能够很简单的处理包界限问题。
经过在包头增长消息体长度的解码器,解析数据时首先获取首部长度,而后定长读取socket中的数据。
换行符解码器,报文尾部增长固定换行符rn,解析数据时以换行符做为报文结尾。
分隔符解码器,使用特定分隔符做为报文的结尾,解析数据时以定义的分隔符做为报文结尾
定长解码器,这个最简单,消息体固定长度,解析数据时按长度读取便可