Welcome to QUIC中的丢包恢复与拥塞控制中文文档

介绍

QUIC (Quick UDP Internet Connection) 是一种建立在UDP协议之上的新型的可以实现 多路复用与安全传输的协议,该协议是从头开始设计的并针对HTTP / 2语义进行了优化。虽然 使用HTTP / 2作为主要应用程序协议构建,但QUIC建立在数十年的传输和安全经验之上,实现 的机制使其成为了一个现代、通用性强且具有吸引力的传输机制。QUIC协议的具体描述可以在文 档‘draft-tsvwg-quic-protocol’中查看。

QUIC实现了已知的TCP丢包恢复机制的核心思想——在RFC,各种Internet草案以及Linux TCP实 现中普遍存在的那些中描述。本文档描述了QUIC丢包恢复机制,并在一些情况下,描述了TCP在其 他的RFC,Internet草案,学术论文和/或TCP具体实现中所描述的一些属性。

QUIC传输机制的设计思想

在QUIC中,所有的传输数据包都与一个包级别的头部一起发送,其中包含数据包序列 号(packet sequence number,下面将会称之为数据包号(packet number)) 这些数据包号是单调递增的,并且在一次连接的生命周期中绝对不会 出现重复的值,这就使得重复检测显得没这么重要了。这一基本的设计决定消除了 传输与重传之间的歧义并降低了在QUIC中实现类似于TCP协议中丢包检测机制的复杂性。

每一个数据包都包含了好几个部分;我们会在下面概述对丢包检测与拥塞控制机制有着 重要联系的部分。

  • STREAM 帧包含了应用数据。加密握手的数据也作为STREAM数据发送,并使用了QUIC 下的可靠性机制。
  • ACK帧包含了确认信息。QUIC使用了一种基于NACK机制的方案,其中最大观测数据包 号( largest_observed packet number)被告知,而数据包序列号小于尚未看到 的最大观测数据包号的分组被报告为NACK范围。ACK帧还包括新确认的每个分组的接收时间戳。

QUIC与TCP之间的相关差异

有一些存在于QUIC与TCP协议之间值得注意的差异,这些差异直接导致了这两种协议采取 的丢包恢复机制的不同。我们将在下面简要地描述这些差异。

单调递增的序列号

TCP将发送方的传输序列号与接收方的传送序列号进行了合并,这将导致同样的数据将携带 同样的序列号,因此导致了“重传歧义(retransmission ambiguity)”的问题。而在QUIC中 则将这两者分了开来:QUIC使用了一个数据包传输号(即为序列号”sequence number”)用 于传输,任何将被传输到正在接收数据的那个应用的数据都将在一条或者多条流中完成,这 些流中的STREAM帧将包含用于确定发送顺序的流偏移值(stream offsets)。

QUIC的数据包序列号时严格递增的,其直接编码了传输的顺序。一个较高的QUIC序列号表 明这个数据包是较晚发送的,而较低的序列号则表明这个数据包是较早发送的。

这样的设计极大的简化了QUIC中的丢包检测机制。大多数TCP机制隐含地尝试基于TCP序列 号推断传输排序;这个任务实现难度不小的,特别是当TCP时间戳不可用时。

QUIC在需要重传时,将以一个新的数据包序列号来重发丢失的数据包,这消除了TCP中当收 到ACK信号时无法确定哪个数据包被确实收到的歧义。因此,可以进行更准确的RTT测量,简 单地检测虚假重传,并且可以仅基于序列号实现诸如快速重传(Fast Retransmit)等的机制。

No SACK Reneging

(注:Reneging的意思就是接收方有权把已经报给发送端SACK里的数据给丢了。)

QUIC中的ACK包含了与TCP中的SACK等价的信息,但是QUIC不允许任何以及被确认接 受的数据包被丢弃,这样不仅可以极大的简化了发送端与接收端的实现难度,还可以减少发送端的内存压力。

更大的NACK范围

相对于TCP的SACK范围为3,QUIC支持的NACK范围高达255。在高丢包率的网络环境下,这将加速数据的恢复。

延迟ACK包的显式校正

QUIC ACK显式的编码在接收分组和发送相应ACK之间在接收器处引起的延迟。这允 许ACK的接收器在估计路径RTT时调整接收器延迟,特别是延迟的ack计时器。 该机制还允许接收端测量并报告从OS的内核收到一个数据包时开始的延迟,这对于 在用户空间的QUIC接收器处理接收的分组之前,可能引起诸如上下文切换等待时间之类的延迟的接收器是有用的。

QUIC的丢包恢复机制概述

我们简要的描述了QUIC在传输、ack接受(ack reception)以及计时器到期( timer expiration)时采取的措施。

发送一个数据包时(On Sending a Packet)

发送一个数据包时

一个重传计时器将会基于以下的规则被设立:

  • 如果握手还没有完成,则开始一个握手计时器

    • SRTT的1.5倍,具有指数退避。
  • 如果存在未完成(outstanding)且已被NACK的未完成数据包,则可能设置丢失计时器

    • 取决于丢失检测实现,在Early Retransmit的情况下默认为0.25RTT。
  • 如果发送的TLP(Tail Loss Probe)少于2个,则计算并重新启动TLP计时器。

    • 如果线路中有多个数据包,则定时器设置为(10ms,2 * SRTT)中的较大值
    • 如果线路中只有一个数据包,则将定时器设置为(1.5*SRTT + delayed ack timer, 2*SRTT)中的较大值
  • 如果发送的TLP达到了两个,则设定RTO计时器

    • 计时器在第一个RTO过了之后将被设定为(200ms, SRTT+4*RTTVAR)中的较大值,且满足指数退避。

接收一个ACK时(On Receiving an ACK)

接收一个ACK时

以下的步骤为当一个ACK被接收时采取的措施:

  • 验证ack,包括忽略任何无序的确认。
  • 更新RTT测量器
  • 发送端以ACKED标记序列号低于最大观测范围(largest_observed)且未被NACK的数据包
  • 数据包编号小于最大观测值(largest_observed)且未被NACK的具有基于FACK递增的missing_reports值。(largest_observed - 丢失数据包编号)
  • 阈值(Threshold)被默认的设定为3
  • 具有missing_reports值且missing_reports > Threshold 的数据包被标记为需要重传。该逻辑一起实现了快速重传和基于FACK的重传。
  • 如果nacked数据包未完成且观察到的最大数据包是最大发送数据包,则重传计时器将设置为0.25SRTT,实现具有计时器的Early Retransmit。
  • 如果不存在未完成的数据包,则停止计时器

计时器停止时(On Timer Expiration)

QUIC使用一个丢失恢复计时器,该计时器在设置时可以处于多种状态之一。 当计时器到期时,状态确定要执行的动作。 (TODO:描述定时器何时设置)

  • 握手(Handshake)状态
    • 重传任何未完成握手的数据包
  • 丢包计时(Loss timer)状态
    • 丢失到目前为止已被NACK的未完成数据包。
    • 将丢包情况报告给拥塞控制器。
    • 在拥塞控制器允许的范围内尽可能多的重传
  • 尾包丢失检测(TLP)状态
    • 重传最小的可重传的未完成数据包。
    • 在ACK到达之前,不要将任何数据包标记为丢失。
    •  重启TLP或RTO的计时器。
  • 重传超时时间(Retransmission TimeOut)状态
    • 不要使拥塞窗口骤减(即:设置为1个数据包),直到ack到达并确认RTO不是虚假的。 请注意,此步骤不需要实施FRTO。
    • 重启下一个RTO的定时器(带指数退避)。

TCP mechanisms in QUIC

RFC 6298 (RTO computation)

FACK Loss Recovery (paper)

RFC 3782, RFC 6582 (NewReno Fast Recovery)

TLP (draft)

RFC 5827 (Early Retransmit) with Delay Timer

RFC 5827 (F-RTO)

RFC 6937 (Proportional Rate Reduction)

TCP Cubic (draft) with optional RFC 5681 (Reno)

Hybrid Slow Start (paper)

Indices and tables