TCP协议是一种可靠的,面向连接的,基于字节流的传输协议。既然是一种可靠的传输协议,那么必须有一种机制来保证接收方收到的数据是完整地,今天聊聊TCP协议下的重传机制。
TCP重传机制包括两部分:超时重传机制和快速重传机制。
背景:TCP的传输确认机制:
假如要传输一个大小为1M的数据包,由于最大传输单元(MTU)的限制,需要将包拆分为N个小包进行传输,并且对每一个包进行编号,这里假如这个N为200,那么编号就为1,2,3,4.....198,199,200
发送方按照编号顺序Seq=1,2,3,4,5,....,200发送给接收方,接收方收到包后需要给接收方发送ACK=2,3,4,5,6....201的确认包,接收方将所有包接收完成之后则进行数据拼接,完成一次传输。由于网络环境的复杂性,这里会涉及到包乱序、重复、丢包问题。
乱序:发送方按照序号Seq=1,2,3,4,5的顺序发包,接收方收到的包可能是1,2,4,3,5
解决方案:接收方发现先收到了序号为4的包,那么会将编号为4的包先缓存起来,等收到了编号为3的包后再将他们按顺序进行排列
丢包:发送方按照序号Seq=1,2,3,4,5的顺序发包,接收方收到的包可能是1,2,3,5
解决方案:接收方发现先收到了编号为5的包,但是编号为4的包迟迟没有收到,那么这里会触发重传机制,让接收方知道,编号为4的包丢掉了,需要重新发,这里的重传机制在下面细讲。
重复包:由于包在网络层有延迟等触发TCP重传,接收方收到的包可能是1,2,3,3,4,5
解决方案:接收方发现收到的编号为3的包已经存在了,那么在此收到编号为3的包,它也不会进行处理,直接丢弃。
超时重传机制,即基于计时器的重传:
顾名思义,简单来讲,超时重传就是发送方设置一个超时时间,如果在这个时间内未收到接收方的ACK确认应答,那么发送方即对包进行重发。
超时时间,我们一般超时时间设置的都比较简单,比如200ms,但是现实中网络环境比较复杂,快一点的传输耗时50ms,这种情况下用200ms来判断超时传输效率会有些低;假设你访问某国外网站,延迟有 130 ms,这就麻烦了,正常的数据包都可能被认为是超时,导致大量数据包被重发,可以想象,重发的数据包也很容易被误判为超时,所以设置固定值是很不可靠的,我们要根据网络延迟,动态调整超时时间,延迟越大,超时时间越长
在这里先引入两个概念:
- RTT(Round Trip Time):往返时延,也就是数据包从发出去到收到对应 ACK 的时间。RTT 是针对连接的,每一个连接都有各自独立的 RTT。
- RTO(Retransmission Time Out):重传超时,也就是前面说的超时时间。
这种机制下,每个数据包都有相应的计时器,一旦超过 RTO 而没有收到 ACK,就重发该数据包。没收到 ACK 的数据包都会存在重传缓冲区里,等到 ACK 后,就从缓冲区里删除。
首先明确一点,对 TCP 来说,超时重传是相当重要的事件(RTO 往往大于两倍的 RTT,超时往往意味着拥塞),一旦发生这种情况,TCP 不仅会重传对应数据段,还会降低当前的数据发送速率,因为TCP 会认为当前网络发生了拥塞。
快速重传机制:
快速重传机制「RFC5681」基于接收端的反馈信息来引发重传,而非重传计时器超时。
刚刚提到过,基于计时器的重传往往要等待很长时间,而快速重传使用了很巧妙的方法来解决这个问题:服务器如果收到乱序的包,也给客户端回复 ACK,只不过是重复的 ACK。就拿刚刚的例子来说,收到乱序的包 6,7,8,9 时,服务器全都发 ACK = 5。这样,客户端就知道 5 发生了空缺。一般来说,如果客户端连续三次收到重复的 ACK,就会重传对应包,而不需要等到计时器超时。