4000 字详解TCP超时与重传,看完没收获算我输( 二 )

标准方法
说实话这个标准方法比较,,,麻烦,我就直接贴公式了:
SRTT <- (1 - α)·SRTT + α·RTT //跟基本方法一样,求 SRTT 的加权平均
rttvar <- (1 - h)·rttvar + h·(|RTT - SRTT |) //计算 SRTT 与真实值的差距(称之为绝对误差|Err|),同样用到加权平均
RTO = SRTT + 4·rttvar //估算出来的新的 RTO,rttvar 的系数 4 是调参调出来的
这个算法的整体思想就是结合平均值(就是基本方法)和平均偏差来进行估算,一波玄学调参得到不错的效果 。如果想更深入了解这个算法,参考「RFC6298」 。
重传——TCP的重要事件基于计时器的重传这种机制下,每个数据包都有相应的计时器,一旦超过 RTO 而没有收到 ACK,就重发该数据包 。没收到 ACK 的数据包都会存在重传缓冲区里,等到 ACK 后,就从缓冲区里删除 。
首先明确一点,对 TCP 来说,超时重传是相当重要的事件(RTO 往往大于两倍的 RTT,超时往往意味着拥塞),一旦发生这种情况,TCP 不仅会重传对应数据段,还会降低当前的数据发送速率,因为TCP 会认为当前网络发生了拥塞 。
简单的超时重传机制往往比较低效,如下面这种情况:

4000 字详解TCP超时与重传,看完没收获算我输

文章插图
 
假设数据包5丢失,数据包 6,7,8,9 都已经到达接收方,这个时候客户端就只能等服务器发送 ACK,注意对于包 6,7,8,9,服务器都不能发送 ACK,这是滑动窗口机制决定的,因此对于客户端来说,他完全不知道丢了几个包,可能就悲观的认为,5 后面的数据包也都丢了,就重传这 5 个数据包,这就比较浪费了 。
快速重传快速重传机制「RFC5681」基于接收端的反馈信息来引发重传,而非重传计时器超时 。
刚刚提到过,基于计时器的重传往往要等待很长时间,而快速重传使用了很巧妙的方法来解决这个问题:服务器如果收到乱序的包,也给客户端回复 ACK,只不过是重复的 ACK 。就拿刚刚的例子来说,收到乱序的包 6,7,8,9 时,服务器全都发 ACK = 5 。这样,客户端就知道 5 发生了空缺 。一般来说,如果客户端连续三次收到重复的 ACK,就会重传对应包,而不需要等到计时器超时 。
4000 字详解TCP超时与重传,看完没收获算我输

文章插图
 
但快速重传仍然没有解决第二个问题:到底该重传多少个包?
带选择确认的重传改进的方法就是 SACK(Selective Acknowledgment),简单来讲就是在快速重传的基础上,返回最近收到的报文段的序列号范围,这样客户端就知道,哪些数据包已经到达服务器了 。
来几个简单的示例:
  • case 1:第一个包丢失,剩下的 7 个包都被收到了 。当收到 7 个包的任何一个的时候,接收方会返回一个带 SACK 选项的 ACK,告知发送方自己收到了哪些乱序包 。注:Left Edge,Right Edge 就是这些乱序包的左右边界 。

4000 字详解TCP超时与重传,看完没收获算我输

文章插图
 
  • case 2:第 2, 4, 6, 8 个数据包丢失 。
    • 收到第一个包时,没有乱序的情况,正常回复 ACK 。
    • 收到第 3, 5, 7 个包时,由于出现了乱序包,回复带 SACK 的 ACK 。
    • 因为这种情况下有很多碎片段,所以相应的 Block 段也有很多组,当然,因为选项字段大小限制, Block 也有上限 。

4000 字详解TCP超时与重传,看完没收获算我输

文章插图
 
不过 SACK 的规范「RFC2018」有点坑爹,接收方可能会在提供一个 SACK 告诉发送方这些信息后,又「食言」,也就是说,接收方可能把这些(乱序的)数据包删除掉,然后再通知发送方 。以下摘自「RFC2018」:
Note that the data receiver is permitted to discard data in its queue that has not been acknowledged to the data sender, even if the data has already been reported in a SACK option. Such discarding of SACKed packets is discouraged, but may be used if the receiver runs out of buffer space.
最后一句是说,当接收方缓冲区快被耗尽时,可以采取这种措施,当然并不建议这种行为 。。。
由于这个操作,发送方在收到 SACK 以后,也不能直接清空重传缓冲区里的数据,一直到接收方发送普通的,ACK 号大于其最大序列号的值的时候才能清除 。另外,重传计时器也收到影响,重传计时器应该忽略 SACK 的影响,毕竟接收方把数据删了跟丢包没啥区别 。
DSACK 扩展DSACK,即重复 SACK,这个机制是在 SACK 的基础上,额外携带信息,告知发送方有哪些数据包自己重复接收了 。DSACK 的目的是帮助发送方判断,是否发生了包失序、ACK 丢失、包重复或伪重传 。让 TCP 可以更好的做网络流控 。


推荐阅读