TCP的SACK(选择性确认)选项格式与传输中的交互流程详解
描述
当TCP连接中出现多个不连续的报文段丢失时,传统的累计确认机制只能确认连续收到的最大序列号,发送方只能重传第一个丢失的包(即使后续非连续包已到达接收方),导致效率低下。SACK(Selective Acknowledgment)通过TCP选项字段,允许接收方明确告知发送方哪些非连续的数据块已成功接收,从而让发送方仅重传真正丢失的报文段,显著改善重传效率。
核心知识点
- SACK选项格式及其编码方式
- 接收方生成SACK块的规则
- 发送方利用SACK信息进行重传决策的流程
步骤1:SACK选项的格式与编码
-
选项基础结构
TCP选项字段由类型(1字节)、长度(1字节)、值(可变长)组成。SACK选项类型值为5,长度字段表示整个选项的字节数。 -
SACK块内容
每个SACK块包含两个32位边界:- 左边界:已接收数据块的起始序列号(包含)
- 右边界:已接收数据块结束序列号的下一个序列号(不包含)
每个SACK块占8字节,加上类型和长度字段,选项最小为10字节(1个SACK块)。
-
多块限制
因TCP头部选项空间有限(最多40字节),通常最多携带3个SACK块(占用26字节:2 + 8×3 = 26)。接收方按接收时间倒序排列SACK块(最近接收的块在前)。
示例:
若接收方收到序列号范围[1000,2000)和[3000,4000)的数据块,SACK选项可编码为:
类型=5, 长度=18, 值=[左边界3000, 右边界4000, 左边界1000, 右边界2000]
步骤2:接收方生成SACK的规则
-
触发条件
当接收方收到乱序或重复的数据段时,会在ACK中携带SACK选项。第一个SACK块总是描述最新接收的非连续数据块。 -
维护SACK信息
接收方维护一个“SACK缓存”,记录当前未被确认的非连续数据块。每次收到新数据段时:- 若序列号连续,则更新累计ACK,并可能清除部分SACK块
- 若序列号不连续,则将新块加入缓存,并按时间倒序更新SACK选项
-
避免冗余
若SACK块之间可合并(如相邻或重叠),接收方应在缓存中合并它们,以节省选项空间。
步骤3:发送方处理SACK的决策流程
-
解析SACK选项
发送方从ACK中提取SACK块,与已发送但未确认的数据段对比,识别哪些数据已成功到达。 -
重传决策(基于SACK的快速重传增强)
- 当收到3个重复ACK(或结合SACK阈值)时,发送方不会只重传第一个丢失段
- 而是检查SACK块,构建“丢失段列表”:所有未被SACK覆盖且未确认的数据段都需要重传
-
重传策略示例
假设发送方发送序列号:1000-1999, 2000-2999, 3000-3999, 4000-4999
接收方收到:1000-1999, 3000-3999 → 回复ACK=2000, SACK块=[3000,4000)
发送方分析:- 累计ACK=2000表示2000之前的都已收到
- SACK块[3000,4000)表示3000-3999已收到
- 缺失段:2000-2999(累计ACK与SACK块之间的空洞)
- 立即重传2000-2999,无需等待后续段超时
-
带宽恢复优化
发送方收到SACK后,可计算“已交付数据量”,在快速恢复阶段更精确地调整拥塞窗口,避免过度减少发送速率。
步骤4:SACK与重复ACK的协同机制
-
重复ACK计数
即使有SACK,重复ACK仍用于触发快速重传。收到第3个重复ACK时,发送方结合SACK信息判断多个丢失段,启动快速重传和快速恢复。 -
SACK重传超时保护
若网络严重乱序导致SACK信息不足,发送方仍依赖RTO超时作为最终重传保障,避免死锁。
实际影响
- 性能提升:在高丢包或乱序网络中,SACK可减少不必要的重传,提升吞吐量30%以上
- 兼容性:SACK在连接建立时通过TCP选项协商(SYN段中的SACK-permitted选项),两端均支持才启用
- 与其它机制协作:SACK常与DSACK(重复SACK)结合,帮助识别虚假重传或网络重复报文
通过以上步骤,SACK在传输层实现了对非连续数据接收的精确反馈,使TCP能适应更复杂的网络环境,是现代TCP实现高可靠、高效率传输的关键特性之一。