目录
粘包与半包是什么
- 粘包:顾名思义,就是原本属于两个包的消息被合并在一起了
- 半包:字面意思,原本是一个包内的消息被拆分成为两个包
为什么会有粘包与半包
出现粘包和半包的首要原因就是TCP是以数据流在网络当中传输的,而“流”是没有明显的收尾区别的,因此在接收的时候也无法区分哪里是数据包的头哪里是尾。
- 粘包产生的两种原因:
1. 每次发送方发送的数据小于socket缓冲区的大小,频繁的发送会被TCP优化算法合并成一个包
2. 接收端接收不及时,使得下一次接收到的包还是上一次遗留的
- 半包产生的两种原因:
1. 发送方发送的数据大于socket缓冲区的大小
2. 发送的数据大于协议的 MTU (Maximum Transmission Unit,最大传输单元),因此必须拆包。
如何解决粘包以及半包问题
1. 告诉接收方包的“头尾”
既然粘包和半包的根本原因是接收方不知道发送的数据流的收尾,那么在发送的时候告诉接收方的发送方数据的大小就可以解决。具体做法就是在发送的TCP包前面再封装一层,就是加上数据包的长度data=str(len(data))+data
,但是这样做的缺点就是编码成本较大,会增加程序的运行时间。
固定缓冲区的大小
发送方每次将不足缓冲区大小的数据包使用空字符填充,接收方每次接受也只接受固定长度的包长。这样做虽然可以解决粘包的问题,但是对于拆包的问题却不能很好的解决,同时会对网络产生额外的开销。
特殊字符结尾
在每个数据包的结尾加上特殊的字符,例如\n\n\t\t
,这样接收方每次提取到这几个特殊的字符都会认为是一个完整的包结束了。可以解决粘包问题,同时对于半包(拆包)问题,也可以得到很好的解决。
后话
为什么TCP会发生粘包和拆包问题,UDP却不会?
UDP是面向无连接的协议,每个UDP段都是一条消息,应用程序必须以消息为单位提取数据,不能一次提取任一字节的数据,也就不存在粘包和拆包的问题。同时UDP致力于高效率服务。不会使用块的合并优化算法,, 由于UDP支持的是一对多的模式,所以接收端的socket buffer(套接字缓冲区)采用了链式结构来记录每一个到达的UDP包,在每个UDP包中就有了消息头(消息来源地址,端口等信息),这样,对于接收端来说,就容易进行区分处理了。 即面向消息的通信是有消息保护边界的。
为什么会发生粘包和拆包的TCP反而被称为可靠的连接与传输
TCP的“可靠”是体现在它是面向连接的传输;以及它在传输前的三次握手,传输结束之后的四次挥手。并且TCP传输是不会丢包的。