Skip to main content

5. Estimating the Round-Trip Time (往返时间估算)

在较高层面上, 端点测量从数据包发送到被确认之间的时间作为 RTT 样本 (RTT Sample). 端点使用 RTT 样本和对端报告的主机延迟 (Host Delays, 参见 [QUIC-TRANSPORT] 的第 13.2 节) 来生成网络路径 RTT 的统计描述. 端点为每条路径计算以下三个值: 一段时间内的最小值 (min_rtt), 指数加权移动平均值 (Exponentially Weighted Moving Average, smoothed_rtt), 以及观察到的 RTT 样本中的平均偏差 (Mean Deviation, 在本文档的其余部分中称为"变化量" variation, rttvar).

5.1. Generating RTT Samples (生成 RTT 样本)

当接收到满足以下两个条件的 ACK 帧时, 端点生成一个 RTT 样本:

  • 最大已确认数据包号 (Largest Acknowledged Packet Number) 是新确认的, 并且

  • 至少有一个新确认的数据包是确认触发的 (Ack-Eliciting).

RTT 样本 latest_rtt 生成为自最大已确认数据包发送以来经过的时间:

latest_rtt = ack_time - send_time_of_largest_acked

RTT 样本仅使用接收到的 ACK 帧中最大的已确认数据包生成. 这是因为对端仅报告 ACK 帧中最大已确认数据包的确认延迟 (Acknowledgment Delays). 虽然报告的确认延迟不用于 RTT 样本测量, 但它用于在后续的 smoothed_rttrttvar 计算中调整 RTT 样本 (第 5.3 节).

为了避免为单个数据包生成多个 RTT 样本, 如果 ACK 帧没有新确认最大的已确认数据包, 则不应该 (SHOULD NOT) 使用该 ACK 帧更新 RTT 估计.

当接收到的 ACK 帧没有新确认至少一个确认触发数据包时, 禁止 (MUST NOT) 生成 RTT 样本. 当仅接收到非确认触发数据包时, 对端通常不会发送 ACK 帧. 因此, 仅包含非确认触发数据包确认的 ACK 帧可能包含任意大的 ACK Delay 值. 忽略此类 ACK 帧可以避免后续 smoothed_rttrttvar 计算中的复杂性.

当在一个 RTT 内接收到多个 ACK 帧时, 发送方可能每个 RTT 生成多个 RTT 样本. 如 [RFC6298] 中所建议的, 这样做可能导致 smoothed_rttrttvar 中的历史不足. 确保 RTT 估计保留足够的历史是一个开放的研究问题.

5.2. Estimating min_rtt (估算 min_rtt)

min_rtt 是发送方对给定网络路径在一段时间内观察到的最小 RTT 的估计. 在本文档中, min_rtt 被丢包检测用于拒绝不合理的小 RTT 样本.

min_rtt 必须 (MUST) 在第一个 RTT 样本时设置为 latest_rtt. 在所有其他样本时, min_rtt 必须 (MUST) 设置为 min_rttlatest_rtt (第 5.1 节) 中的较小者.

端点在计算 min_rtt 时仅使用本地观察到的时间, 并且不会针对对端报告的确认延迟进行调整. 这样做允许端点完全基于其观察到的内容为 smoothed_rtt 设置下限 (参见第 5.3 节), 并限制由于对端错误报告的延迟导致的潜在低估.

网络路径的 RTT 可能随时间变化. 如果路径的实际 RTT 减少, min_rtt 将在第一个较低样本上立即适应. 然而, 如果路径的实际 RTT 增加, min_rtt 将不会适应它, 允许小于新 RTT 的未来 RTT 样本被包含在 smoothed_rtt 中.

端点应该 (SHOULD) 在建立持续拥塞后将 min_rtt 设置为最新的 RTT 样本. 这避免了在 RTT 增加时重复声明持续拥塞. 这还允许连接在破坏性网络事件后重置其对 min_rttsmoothed_rtt 的估计; 参见第 5.3 节.

端点可以 (MAY) 在连接的其他时间重新建立 min_rtt, 例如当流量较小且接收到具有低确认延迟的确认时. 实现不应该 (SHOULD NOT) 过于频繁地刷新 min_rtt 值, 因为路径的实际最小 RTT 并不经常可观察到.

5.3. Estimating smoothed_rtt and rttvar (估算 smoothed_rtt 和 rttvar)

smoothed_rtt 是端点 RTT 样本的指数加权移动平均值, rttvar 使用平均变化量估算 RTT 样本中的变化.

smoothed_rtt 的计算使用调整确认延迟后的 RTT 样本. 这些延迟从 ACK 帧的 ACK Delay 字段解码, 如 [QUIC-TRANSPORT] 的第 19.3 节所述.

对端可能会在握手期间报告大于对端 max_ack_delay 的确认延迟 ([QUIC-TRANSPORT] 的第 13.2.1 节). 为了解决这个问题, 端点应该 (SHOULD) 在握手确认之前忽略 max_ack_delay, 如 [QUIC-TLS] 的第 4.1.2 节所定义. 当这些大的确认延迟发生时, 它们可能是非重复的并且仅限于握手. 因此, 端点可以使用它们而不将它们限制为 max_ack_delay, 从而避免 RTT 估计的不必要膨胀.

请注意, 如果对端的确认延迟报告或端点的 min_rtt 估计存在错误, 较大的确认延迟可能会导致 smoothed_rtt 大幅膨胀. 因此, 在握手确认之前, 如果调整 RTT 样本的确认延迟会导致样本小于 min_rtt, 则端点可以 (MAY) 忽略 RTT 样本.

握手确认后, 对端报告的任何大于对端 max_ack_delay 的确认延迟都归因于无意但可能重复的延迟, 例如对端的调度器延迟或先前确认的丢失. 过度的延迟也可能是由于不合规的接收方造成的. 因此, 这些额外的延迟被有效地视为路径延迟的一部分, 并被纳入 RTT 估计.

因此, 当使用对端报告的确认延迟调整 RTT 样本时, 端点:

  • 可以 (MAY) 忽略 Initial 数据包的确认延迟, 因为对端不会延迟这些确认 ([QUIC-TRANSPORT] 的第 13.2.1 节);

  • 应该 (SHOULD) 在握手确认之前忽略对端的 max_ack_delay;

  • 必须 (MUST) 在握手确认后使用确认延迟和对端 max_ack_delay 中的较小者; 并且

  • 如果结果值小于 min_rtt, 则禁止 (MUST NOT) 从 RTT 样本中减去确认延迟. 这限制了由于误报对端导致的 smoothed_rtt 低估.

此外, 当相应的解密密钥不能立即使用时, 端点可能会推迟处理确认. 例如, 客户端可能会收到对 0-RTT 数据包的确认, 但无法解密, 因为 1-RTT 数据包保护密钥尚不可用. 在这种情况下, 端点应该 (SHOULD) 在握手确认之前从其 RTT 样本中减去此类本地延迟.

类似于 [RFC6298], smoothed_rttrttvar 的计算如下.

端点在连接建立期间和在连接迁移期间重置估算器时初始化 RTT 估算器; 参见 [QUIC-TRANSPORT] 的第 9.4 节. 在新路径的任何 RTT 样本可用之前或重置估算器时, 使用初始 RTT 初始化估算器; 参见第 6.2.2 节.

smoothed_rttrttvar 初始化如下, 其中 kInitialRtt 包含初始 RTT 值:

smoothed_rtt = kInitialRtt
rttvar = kInitialRtt / 2

网络路径的 RTT 样本记录在 latest_rtt 中; 参见第 5.1 节. 在初始化后的第一个 RTT 样本时, 使用该样本重置估算器. 这确保估算器不保留过去样本的历史. 在其他路径上发送的数据包不会为当前路径贡献 RTT 样本, 如 [QUIC-TRANSPORT] 的第 9.4 节所述.

在初始化后的第一个 RTT 样本时, smoothed_rttrttvar 设置如下:

smoothed_rtt = latest_rtt
rttvar = latest_rtt / 2

在后续的 RTT 样本上, smoothed_rttrttvar 演化如下:

ack_delay = decoded acknowledgment delay from ACK frame
if (handshake confirmed):
ack_delay = min(ack_delay, max_ack_delay)
adjusted_rtt = latest_rtt
if (latest_rtt >= min_rtt + ack_delay):
adjusted_rtt = latest_rtt - ack_delay
smoothed_rtt = 7/8 * smoothed_rtt + 1/8 * adjusted_rtt
rttvar_sample = abs(smoothed_rtt - adjusted_rtt)
rttvar = 3/4 * rttvar + 1/4 * rttvar_sample