TCP 的断包和粘包
以太网中存在一个对于帧的有效数据大小的限制,即 MTU,以太网的 MTU 为 1500 字节。
一、断包
就是说发送端一次发送的消息长度过大,如果超过了 MTU,那么 ip 会对其进行分片。
在网络编程中,要避免出现 IP 分片。因为是 IP 层是没有超时重传机制的,如果 IP 层对一个数据包进行了分片,只要有一个分片丢失了,只能依赖于传输层进行重传,结果是所有的分片都要重传一遍,这个代价有点大。由此可见,IP 分片会大大降低传输层传送数据的成功率,所以要避免 IP 分片。
对于 UDP 包,我们需要在应用层去限制每个包的大小,一般不要超过 1472 字节,即以太网 MTU(1500)- UDP 首部(8)- IP 首部(20)。
对于 TCP 数据,应用层就不需要考虑这个问题了,因为传输层已经做了。在建立连接的三次握手的过程中,连接双方会相互通告MSS(Maximum Segment Size,最大报文段长度),一般 MSS = MTU - IP 首部(20)- TCP 首部(20),每次发送的 TCP 数据都不会超过双方 MSS 的最小值,所以就保证了 IP 数据报不会超过 MTU,避免了 IP 分片。
而断包就是因为 MSS 的存在,当消息长度过大,例如超过了 1460 字节(因为 tcp 首部一般为 20 个字节,ip 首部为 20 个字节),那么 tcp 就会将其分片,然后每片被 tcp 封装,然后由 ip 封装,最后被传输到接收端,这样子当接收端接收到消息后,就会不清楚这是不是一个完整的消息。
二、粘包
为了提高网络利用率,当传输层发现传输的数据长度太小时,会等待多个消息一起发送,这时候就会提高网络利用率,但是当接收端接收过以后,会不知道这是一个完整的消息,还是多个消息在一起。从而有可能将其作为一个消息来处理。nagle 算法就是实现的这个功能。
对于断包和粘包的通常处理方法为将消息封装为一定的格式,例如每个消息头部为 aa,尾部为 55,或者将整个消息的有效长度标明,这样子当接收端接收到消息之后,就可以以此来分辨消息是不是我完整的。