上一篇介绍 TCP 的文章「

图上列了两种情况,这两种情况下计算 RTT 的方法是不一样的(这就是所谓的重传二义性):

  • 情况一:RTT = t2 - t0
  • 情况二:RTT = t2 - t1

但是对于客户端来说,它不知道发生了哪种情况,选错情况的结果就是 RTT 偏大/偏小,影响到 RTO 的计算。(最简单粗暴的解决方法就是忽略有重传的数据包,只计算那些没重传过的,但这样会导致其他问题。。详见 Karn's algorithm

  • 另一个问题是,这个算法假设 RTT 波动比较小,因为这个加权平均的算法又叫低通滤波器,对突然的网络波动不敏感。如果网络时延突然增大导致实际 RTT 值远大于估计值,会导致不必要的重传,增大网络负担。( RTT 增大已经表明网络出现了过载,这些不必要的重传会进一步加重网络负担)。

  • 标准方法

    说实话这个标准方法比较,,,麻烦,我就直接贴公式了:

    ​ 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 会认为当前网络发生了拥塞。

    简单的超时重传机制往往比较低效,如下面这种情况:

    假设数据包5丢失,数据包 6,7,8,9 都已经到达接收方,这个时候客户端就只能等服务器发送 ACK,注意对于包 6,7,8,9,服务器都不能发送 ACK,这是滑动窗口机制决定的,因此对于客户端来说,他完全不知道丢了几个包,可能就悲观的认为,5 后面的数据包也都丢了,就重传这 5 个数据包,这就比较浪费了。

    快速重传

    快速重传机制「

    但快速重传仍然没有解决第二个问题:到底该重传多少个包?

    带选择确认的重传

    改进的方法就是 SACK(Selective Acknowledgment),简单来讲就是在快速重传的基础上,返回最近收到的报文段的序列号范围,这样客户端就知道,哪些数据包已经到达服务器了。

    来几个简单的示例:

                  Triggering    ACK      Left Edge   Right Edge              Segment               5000         (lost)              5500         5000     5500       6000              6000         5000     5500       6500              6500         5000     5500       7000              7000         5000     5500       7500              7500         5000     5500       8000              8000         5000     5500       8500              8500         5000     5500       9000 
               Triggering  ACK    First Block   2nd Block     3rd Block           Segment            Left   Right  Left   Right  Left   Right                              Edge   Edge   Edge   Edge   Edge   Edge            5000       5500           5500       (lost)           6000       5500    6000   6500           6500       (lost)           7000       5500    7000   7500   6000   6500           7500       (lost)           8000       5500    8000   8500   7000   7500   6000   6500           8500       (lost)

    不过 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.