计算机网络笔记(三十四)——5.6TCP可靠传输的实现
5.6.1以字节为单位的滑动窗口
一、滑动窗口的核心设计目的
- 提升效率:解决停等协议(每次发报文必须等待 ACK)的低效问题,允许批量发送数据和批量确认
- 流量控制:接收方通过窗口大小动态调整发送方的发送速率,防止接收方缓冲区溢出
- 按序到达:通过字节序号管理保证数据的顺序性
二、核心概念图解
发送方窗口和接收方窗口的示意图:
三、窗口参数定义(字节级别)
1. 发送方核心参数
参数 | 描述 |
---|---|
SND.WND | 接收方通告的接收窗口大小(动态变化) |
SND.UNA | 最早的未确认字节序号(窗口左边界) |
SND.NXT | 下一个要发送的字节序号(窗口右边界) |
可用窗口 | = SND.WND - (SND.NXT - SND.UNA) |
2. 接收方核心参数
参数 | 描述 |
---|---|
RCV.WND | 当前接收窗口大小(影响通告值) |
RCV.NXT | 期望接收的下一个字节序号 |
四、窗口运行动态流程
五、关键行为规则
-
发送窗口滑动条件:
- 收到新的 ACK 确认时,左边界
SND.UNA
向右移动 - 示例:若收到 ACK=1500,窗口左边界从 1000 移动到 1500
- 收到新的 ACK 确认时,左边界
-
接收窗口调整规则:
- 每次读取缓冲区数据后,右边界扩展(
RCV.NXT
右移) - 通过 TCP 头部的
Window
字段向对端通告最新窗口大小
- 每次读取缓冲区数据后,右边界扩展(
-
零窗口处理:
- 接收方窗口=0 时,发送方启动持续计时器(Persist Timer)
- 定期发送 1 字节探测报文,检测窗口是否重新开放
六、典型问题场景
场景 1:发送窗口耗尽
场景 2:接收方处理变慢
七、与 RFC 定义的关联
- RFC 793(TCP 基础规范)定义滑动窗口基本机制
- RFC 1323(窗口扩展选项)支持超过 65KB 的窗口大小
- RFC 7323(TCP 扩展)优化窗口管理算法
5.6.2超时重传时间的选择
一、核心概念
-
RTT(Round-Trip Time)
- 数据包从发送到接收确认(ACK)的往返时间。
- RTT是动态变化的,受网络延迟、拥塞等因素影响。
-
RTO(Retransmission Timeout)
- 超时重传时间,即发送方等待ACK的最大时间。若超时未收到ACK,触发重传。
- 目标:略大于当前网络的RTT,避免过早重传或不必要等待。
二、RTO计算原理
1. 动态估算RTT
通过平滑RTT(SRTT) 和RTT偏差(DevRTT) 动态调整RTO,公式如下:
SRTT = (1 - α) * SRTT + α * SampleRTT
DevRTT = (1 - β) * DevRTT + β * |SampleRTT - SRTT|
RTO = SRTT + 4 * DevRTT
- α、β:平滑因子(RFC 6289建议α=1/8,β=1/4)。
- SampleRTT:最新测量的实际RTT值。
2. 初始RTO
- 未采集到RTT样本时,默认初始RTO为1秒(Linux)。
- 当首次获得SampleRTT后,SRTT=SampleRTT,DevRTT=SampleRTT/2。
3. 超时后的退避机制
- 每次超时后,RTO按指数增长:
新RTO = 旧RTO * 2
。 - 避免拥塞加剧,逐步试探网络的恢复情况。
三、关键流程
流程示意图
流程解析
- 发送数据包:启动定时器,记录发送时间。
- 接收ACK:
- 计算SampleRTT(当前时间 - 包发送时间)。
- 更新SRTT和DevRTT。
- 重新计算RTO = SRTT + 4×DevRTT。
- 未收到ACK且超时:
- 触发重传,RTO翻倍。
- 避免立即使用新RTT样本(Karn算法,排除重传干扰)。
四、举例说明
假设初始RTT样本为200ms,α=0.125,β=0.25:
步骤 | 事件 | 计算过程 | RTO结果 |
---|---|---|---|
1 | 首次SampleRTT=200ms | SRTT=200ms, DevRTT=100ms → RTO = 200+4×100=600ms | 600ms |
2 | 新SampleRTT=250ms | SRTT=(0.875×200)+(0.125×250)=206.25ms DevRTT=(0.75×100)+0.25×|250 - 206.25| | 250-206.25 |
3 | 超时重传 | RTO翻倍(如原RTO 550ms → 新RTO 1100ms) | 1100ms |
五、重要细节
-
Karn算法
- 重传时丢弃对应SampleRTT,避免因ACK对应原始包还是重传包而产生误差。
-
Linux内核参数
tcp_retries2
控制最大重传次数,默认15次。- 初始RTO为1秒,动态调整后上限通常为120秒。
-
三次握手阶段的RTO
- SYN报文初始RTO为1秒,后续指数退避(1s, 3s, 7s…)。
六、总结
- 核心逻辑:通过动态估算RTT均值(SRTT)和波动(DevRTT),确保RTO适应网络变化。
- 设计目标:平衡及时重传与网络拥塞控制,保障传输效率和可靠性。
5.6.3选择确认SACK
一、为什么需要SACK?
在传统TCP协议的累积确认机制中,接收方只能通过ACK
确认连续接收到的数据。例如:
- 发送方发送序列号1-1000、1001-2000、2001-3000的数据;
- 若中间段1001-2000丢失,接收方只能持续回复ACK=1001;
- 发送方被迫重传
1001-3000
所有数据(即使2001-3000已到达)。
SACK(Selective Acknowledgment)允许接收方明确告知发送方哪些数据块已成功接收,发送方只需重传实际丢失的部分,提升效率。
二、SACK工作机制
1. 报文结构
SACK通过TCP头部选项字段实现,格式如下:
| Kind=5 | Length | Left Edge 1 | Right Edge 1 | ... | Left Edge N | Right Edge N |
- Kind=5:标识该选项为SACK;
- Length:选项总长度;
- Left Edge/Right Edge:表示接收方已成功接收的数据块区间(左闭右开)。
2. 交互流程
SACK在数据丢失时的处理流程:
- 发送方发送连续的4个数据段;
- 第二个数据段丢失,后续第三个和第四个被接收方缓存;
- 接收方发送ACK=1001,并携带SACK块
2001-4000
(已接收的非连续数据区间); - 发送方仅需重传丢失的1001-2000段;
- 接收方收到后发送累积确认ACK=4001。
3. 附加规则
- SACK需与普通ACK配合使用,ACK仍指向连续数据的最高字节;
- 最多可携带3个SACK块(受TCP选项字段长度限制);
- 接受方需维护接收缓冲区列表以跟踪非连续数据块。
三、SACK对性能的影响
- 减少冗余重传:仅重传缺失的块,避免带宽浪费;
- 适应高丢包率场景:在无线网络或拥塞链路中表现更好;
- 支持乱序接收:允许非连续数据块标记,提升吞吐量。
四、配置与实现
- Linux启用SACK:默认开启,可通过
net.ipv4.tcp_sack
参数调整; - 发送方策略:需实现接收SACK块解析,并维护未确认数据的SACK映射。例如,联合使用
快速重传
和SACK来优化网络效率。
关键总结
通过SACK机制,TCP实现了对非连续数据块的精确确认,在高丢包或乱序场景下大幅提升传输效率。