TCP 粘包 拆包
Contents
TCP 粘(nián)包 拆包
这两个词并没有一一对应的英文
tcp 文档中并不存在 粘包拆包的描述, 一般粘包抓包是指应用层协议的边界定义和数据报读取/处理的问题
tcp是面向流的协议, 在tcp上接收数据报(datagram) 就需要处理 流(stream) 到 数据报的过程.
TCP是面向字节流的协议,就是没有界限的一串数据,本没有“包”的概念,“粘包”和“拆包”一说是为了有助于形象地理解这两种现象。
粘包拆包发生场景
因为TCP是面向流,没有边界,而操作系统在发送TCP数据时,会通过缓冲区来进行优化,例如缓冲区为1024个字节大小。
如果一次请求发送的数据量比较小,没达到缓冲区大小,TCP则会将多个请求合并为同一个请求进行发送,这就形成了粘包问题。
如果一次请求发送的数据量比较大,超过了缓冲区大小,TCP就会将其拆分为多次发送,这就是拆包。
正常的理想情况,两个包恰好满足TCP缓冲区的大小或达到TCP等待时长,分别发送两个包; 粘包:两个包较小,间隔时间短,发生粘包,合并成一个包发送; 拆包:一个包过大,超过缓存区大小,拆分成两个或多个包发送; 拆包和粘包:Packet1过大,进行了拆包处理,而拆出去的一部分又与Packet2进行粘包处理。
粘包
TCP协议中,发送方发送的若干包数据到接收方接收时粘成一包,从接收缓冲区看,后一包数据的头紧接着前一包数据的尾。
TCP网络通信时候会发生粘包/拆包的问题,接下来探讨其解决之道。
什么是粘包/拆包
一般所谓的 TCP 粘包是在一次接收数据不能完全地体现一个完整的消息数据。 TCP通讯为何存在粘包呢?主要原因是TCP是以流的方式来处理数据,再加上网络上 MTU 的往往小于在应用处理的消息数据,所以就会引发一次接收的数据无法满足消息的需要,导致粘包的存在。 处理粘包的唯一方法就是制定应用层的数据通讯协议,通过协议来规范现有接收的数据是否满足消息数据的需要。
情况分析
TCP 粘包通常在流传输中出现,UDP 则不会出现粘包,因为 UDP 有消息边界,发送数据段需要等待缓冲区满了才将数据发送出去,当满的时候有可能不是一条消息而是几条消息合并在换中去内,在成粘包;另外接收数据端没能及时接收缓冲区的包,造成了缓冲区多包合并接收,也是粘包。
解决办法
- 消息定长,报文大小固定长度,不够空格补全,发送和接收方遵循相同的约定,这样即使粘包了通过接收方编程实现获取定长报文也能区分。
- 包尾添加特殊分隔符,例如每条报文结束都添加回车换行符 (例如FTP协议) 或者指定特殊字符作为报文分隔符,接收方通过特殊分隔符切分报文区分。例如,FTP协议;
- 将消息分为消息头和消息体,消息头中包含表示信息的总长度 (或者消息体长度) 的字段
- 更复杂的自定义应用层协议
http://my.oschina.net/imhoodoo/blog/357290
为什么UDP没有粘包? 粘包拆包问题在数据链路层、网络层以及传输层都有可能发生。日常的网络应用开发大都在传输层进行,由于UDP有消息保护边界,不会发生粘包拆包问题,因此粘包拆包问题只发生在TCP协议中。
Netty对粘包和拆包问题的处理 Netty对解决粘包和拆包的方案做了抽象,提供了一些解码器 (Decoder)来解决粘包和拆包的问题。如:
LineBasedFrameDecoder:以行为单位进行数据包的解码; DelimiterBasedFrameDecoder:以特殊的符号作为分隔来进行数据包的解码; FixedLengthFrameDecoder:以固定长度进行数据包的解码; LenghtFieldBasedFrameDecode:适用于消息头包含消息长度的协议 (最常用); 基于Netty进行网络读写的程序,可以直接使用这些Decoder来完成数据包的解码。对于高并发、大流量的系统来说,每个数据包都不应该传输多余的数据 (所以补齐的方式不可取),LenghtFieldBasedFrameDecode更适合这样的场景。
小结 TCP协议粘包拆包问题是因为TCP协议数据传输是基于字节流的,它不包含消息、数据包等概念,需要应用层协议自己设计消息的边界,即消息帧 (Message Framing)。如果应用层协议没有使用基于长度或者基于终结符息边界等方式进行处理,则会导致多个消息的粘包和拆包。
虽然很多框架中都有现成的解决方案,比如Netty,但底层的原理我们还是要清楚的,而且还要知道有这么会事,才能更好的结合场景进行使用。
Author -
LastMod 2015-09-17