Skip to content

模型Model

QUIC包号与确认区间

QUIC packet number spaces · QUIC ACK ranges

把包号空间、已处理包集合和确认区间分开记账,逐项解码Gap并区分确认、交付与遗忘。

形式陈述 ​

一次发送有自己的编号 ​

多流重组已经把“这次发送的是哪个包”与“载荷写入哪条流的哪些字节”分开。本页继续使用连接、方向、包号空间组成的包身份;新增的任务是:接收端如何报告已经处理过的包,发送端怎样解释这份报告。假定包已成功解除保护并通过帧处理,连接没有重启;不讨论密钥计算、截断包号恢复或连接迁移。

QUIC v1有三个包号空间:Initial、Handshake、Application Data。0-RTT和1-RTT属于同一个Application空间,1-RTT更新密钥也不另开一份包号。每个发送方向在每个空间从0开始,后续使用的包号严格增大,允许跳过一些号,但不能复用。编号到达协议上限 262−1 时须按规范终止继续发送,不能绕回0。[1, §12.3]

收到ACK时,承载该ACK的包所属空间决定它确认哪个空间;方向则是对端此前发送的反方向。例如服务器在Handshake包7里确认客户端Handshake包12,不会确认客户端Application包12。ACK自己的承载包号7,也不是被确认包号12。

区间按包号从高到低排列 ​

用闭区间 [l,h] 表示从 l 到 h 的所有整数包号。ACK先携带Largest Acknowledged H 和First ACK Range F,给出第一段

[H−F,H].

再按从高到低的顺序携带若干对 (g,a)。已知上一段的最小包号为 l,下一段为

h′=l−g−2,l′=h′−a,[l′,h′].

其中Gap字段 g 表示中间有 g+1 个本次未确认的包号;Range字段 a 表示该段有 a+1 个已确认包号。所有字段是非负整数,ACK Range Count是后续二元组的数目,不含第一段。任何计算得到负包号都构成FRAME_ENCODING_ERROR。这里直接使用解码后的整数,不重写可变长整数的字节编码。[1, §19.3.1]

正向事实不等于整个历史 ​

令本端在某空间实际发送过的包号集合为 T,此前已经确认的集合为 A,本次ACK解码得到 R。教学检查器保留完整有限历史,先验证 R⊆T,再更新 A←A∪R;对可检测的未发送包确认予以报错。真实规范要求能检测到这种情况时应当按PROTOCOL_VIOLATION处理。[1, §13.1]

集合 T∖A 中还有“未被确认”的包,并不自动表示已经丢失。集合 A 单调增长;一个后续ACK没有重复某个旧范围,不会撤销已获得的确认。接收端必须在成功处理整个包后才能确认;对STREAM数据,这只要求已进入供应用读取的队列,并不要求应用已经消费。[1, §§13.1、13.2.3]

直觉

ACK列的是收到的岛,空隙仍需判断 ​

把已处理包号画在数轴上,相邻号码连成一段。ACK把这些“岛”的右端、长度和相邻岛间空隙写出来。Largest是最右侧的一点,不是“所有更小包号都已收到”的承诺。

一个包可以装两条流的帧;确认这个包,说明这次发送携带的帧已经被处理。反过来,同一段流字节可能放在两个不同包里补发。确认其中一个包足以给出该份数据的交付证据,但另一个包仍有自己的包级记录,不能把两个包号合并成一次发送。

ACK区间中的加一与减二
例子与边界

六个收到的号怎样写成三个区间 ​

某客户端已发送Application包0至12。服务器成功处理的集合为 {2,3,4,7,8,12},写成降序区间 [12,12]、[7,8]、[2,4]。编码如下。

字段 值 解码动作
Largest 12 第一段最高为12
First Range 0 第一段最低 12−0=12
Range Count 2 还有两组Gap/Range
第一组Gap、Range 2、1 最高 12−2−2=8,最低 8−1=7
第二组Gap、Range 1、2 最高 7−1−2=4,最低 4−2=2

第一处空隙是9、10、11,共3个号,所以Gap为2。若错用“下一最高=上一最低−Gap−1”,就会把9误确认为已收到。这个差一错误不仅会多出一个号,还会让发送端误删它的恢复责任。

下一份ACK只有 [12,12] 时,发送者仍保留2、3、4、7、8已确认的事实。若随后收到8的重复包,接收者不能再次执行它包含的应用操作;重复抑制与ACK报告如何压缩,是相关但不同的状态职责。

缺号和错误号各说明什么 ​

包5不在ACK里,可能尚在途中、已经丢失,或不在这次保留的确认范围内。发送者不能仅从一次遗漏就判定网络把它丢掉。包号还允许被主动跳过;例如实际 T={0,2},收到只确认2的ACK没有义务等待1,因为1从未发送。

若Largest为3、First为4,第一段最小值为−1,编码立即非法。若Largest为3、First为0,后续Gap为2,则下一最高为−1,同样非法。检查负数应在删除任何发送记录之前完成。本文检查器还拒绝二元组数量与Range Count不符的输入,避免把截短记录当成一份完整ACK。

丢掉确认历史以后,不能重新接受旧包 ​

有限接收端不可能永久保存每个历史包号。RFC允许缩减ACK范围,但要求一旦丢弃某范围,就确保不会随后再次接受那个范围内的包。一种具体策略是维护单调下界 m:先把 m 以下号码全部纳入“以后直接丢弃”,再释放这些号码的区间和重复检测状态。只清空Seen却仍接受旧包,会让重复包重新执行帧处理。[1, §§12.3、13.2.3]

例如将下界从0推进到7,未来2、3、4连同尚未收到的5、6都将被丢弃;仍记录7、8、12及其间的未收缺口。这里牺牲了低于7的迟到包接纳能力,换取有界历史。流中的尚缺字节仍可在更高的新包号里补发,不能因为释放包号记录就把流缺口当成已填。

推论与应用

确认是恢复控制的输入 ​

QUIC丢包检测会结合已确认的更高包号、发送时刻与RTT估计,对仍未确认的包作阈值判断。ACK提供正向观测,阈值承担推断风险;乱序足够大时,推断也可能错误。

字节流的最终长度仍由流信用与最终大小处理。收到某个FIN所在包的ACK,不会把另一方向也关掉;收到某个普通数据包的ACK,也不说明远端应用已完成一次业务事务。这些分别是发送方向、流状态与应用语义的问题。

处理成本取决于表示和输出 ​

若一帧含 r 个闭区间,直接校验和解码需 O(1+r) 时间及 O(r) 区间输出空间。若把它展开成 q 个独立包号,再逐号查散列表,则需要 O(1+r+q) 期望时间,不能只按区间数收费。真实实现可用有序范围与发送记录交叠查询;本文有限集检查器选择展开以便读者逐个核对,不声称这是无界连接的内存方案。

参考资料

[1] Jana Iyengar、Martin Thomson,RFC 9000: QUIC: A UDP-Based Multiplexed and Secure Transport,2021,§12.3包号空间与重复处理,§13.1成功处理后确认,§13.2.3确认范围管理,§19.3与§19.3.1的字段及递推式。数值集合与报错输入为本页教学实例。

关系图谱4 个相邻概念 · 1 类关系

拖动节点调整位置。

显示关系

显示:依赖

  1. 前置三跳
  2. 前置二跳
  3. 前置一跳
  4. 当前条目
  5. 后续一跳
  6. 后续二跳
  7. 后续三跳
文字版关系按与当前条目的最短距离分组