4. 经典拥塞控制算法:Tahoe 与 Reno
TCP Tahoe 与 Reno 是 TCP 拥塞控制领域最具奠基意义的两个算法。Tahoe 首次完整定义了慢启动、拥塞避免、快速重传三大核心机制;Reno 则在 Tahoe 基础上增加了快速恢复阶段,显著提升了单包丢失场景下的吞吐量。理解这两个算法是掌握后续所有拥塞控制变种(NewReno、BIC、CUBIC 等)的前提。
4.1 核心状态机与关键变量
TCP 拥塞控制状态机由四个阶段组成,每个阶段由拥塞窗口(cwnd)和慢启动阈值(ssthresh)共同驱动。下表总结了各阶段的核心行为:
| 阶段 | 触发条件 | cwnd 更新规则 | ssthresh 更新规则 |
|---|---|---|---|
| 慢启动 | 连接建立或超时重传后 | 每收到一个 ACK,cwnd += 1 MSS(指数增长) | 初始 ssthresh = 拥塞窗口上限(如 65535 字节) |
| 拥塞避免 | cwnd >= ssthresh | 每收到一个 ACK,cwnd += MSS / cwnd(线性增长) | 保持不变 |
| 快速重传 | 收到 3 个重复 ACK(DupACK) | 不直接修改 cwnd,触发重传 | ssthresh = cwnd / 2(Tahoe 与 Reno 相同) |
| 快速恢复 | 仅 Reno:快速重传后进入 | cwnd = ssthresh + 3 MSS(Reno 特有) | 已更新为 cwnd / 2 |
4.2 慢启动与拥塞避免流程
慢启动阶段的目标是快速探测网络可用带宽,拥塞避免阶段则转为保守的线性增长以避免拥塞。下图展示了从连接建立到拥塞避免的完整状态转换:
4.3 快速重传与快速恢复
快速重传机制解决了传统超时重传等待时间过长的问题。当发送端连续收到 3 个重复 ACK 时,立即重传丢失的报文段,无需等待 RTO 超时。Tahoe 与 Reno 在此处分叉:
- Tahoe 行为:进入快速重传后,设置 ssthresh = cwnd / 2,然后将 cwnd 重置为 1 MSS,重新进入慢启动阶段。这种激进的重置策略在单包丢失时过于保守。
- Reno 行为:进入快速重传后,设置 ssthresh = cwnd / 2,但 cwnd 不重置为 1,而是设置为 ssthresh + 3 MSS,进入快速恢复阶段。在快速恢复期间,每收到一个重复 ACK,cwnd 临时增加 1 MSS(用于发送新数据);当收到新 ACK 时,cwnd 降为 ssthresh,退出快速恢复进入拥塞避免。
下表对比了 Tahoe 与 Reno 在丢包恢复时的关键差异:
| 特性 | Tahoe | Reno |
|---|---|---|
| 3 DupACK 后 cwnd | 重置为 1 MSS | 设置为 ssthresh + 3 MSS |
| 恢复阶段 | 无快速恢复,直接进入慢启动 | 有快速恢复阶段 |
| 单包丢失吞吐量 | 较低(需重新慢启动) | 较高(保持管道半满) |
| 多包丢失处理 | 可能触发多次超时 | 可能退化为超时(Reno 限制) |
| Linux 内核实现 | 仅历史参考 | tcp_reno_cong_avoid() |
4.4 快速恢复详细流程
Reno 快速恢复是理解 TCP 拥塞控制状态机复杂性的关键。以下伪代码展示了 Linux 内核中快速恢复的核心逻辑:
/* Linux 内核 TCP Reno 快速恢复伪代码 */
void tcp_reno_fast_recovery(struct sock *sk, struct sk_buff *skb) {
struct tcp_sock *tp = tcp_sk(sk);
/* 1. 进入快速恢复:设置 ssthresh = cwnd / 2 */
tp->snd_ssthresh = max(tp->snd_cwnd >> 1U, 2U);
/* 2. 设置 cwnd = ssthresh + 3 MSS(已收到的 3 个 DupACK 允许发送新数据) */
tp->snd_cwnd = tp->snd_ssthresh + 3;
/* 3. 重传丢失的报文段(由快速重传触发) */
tcp_retransmit_skb(sk, skb);
/* 4. 进入快速恢复循环 */
tp->recovery = 1;
/* 5. 每收到一个重复 ACK,临时增加 cwnd */
while (收到重复 ACK) {
tp->snd_cwnd++;
/* 允许发送新数据 */
}
/* 6. 收到新 ACK(确认了丢失报文段的重传) */
if (收到新 ACK) {
/* 退出快速恢复 */
tp->snd_cwnd = tp->snd_ssthresh;
tp->recovery = 0;
/* 进入拥塞避免 */
}
}
4.5 状态机完整转换图
下图整合了 Tahoe 与 Reno 的完整状态机,突出显示两者在丢包恢复路径上的分叉:
4.6 内核实现关键点
在 Linux 内核网络协议栈中,Tahoe 与 Reno 的实现集中在 net/ipv4/tcp_cong.c 和 net/ipv4/tcp_input.c 中。以下是几个关键实现细节:
- 拥塞避免函数:
tcp_reno_cong_avoid()实现了 AIMD(加法增乘法减)逻辑,每收到一个 ACK 增加 cwnd 的 1/cwnd。 - 快速重传触发:在
tcp_ack()中检测重复 ACK 计数,当达到TCP_FASTRETRANS_THRESH(默认为 3)时调用tcp_fastretrans_alert()。 - 快速恢复入口:
tcp_enter_recovery()设置tcp->recovery标志,并调用拥塞控制算法的cong_control回调。 - ssthresh 更新:通过
tcp_reno_ssthresh()实现,返回max(cwnd / 2, 2)。
sysctl net.ipv4.tcp_congestion_control 查看当前拥塞控制算法。对于 Reno 算法,关键可调参数是 net.ipv4.tcp_retries1 和 net.ipv4.tcp_retries2,它们影响快速重传和超时重传的阈值。在低延迟网络中,适当降低这些值可以更快地检测丢包。
4.7 Tahoe 与 Reno 的适用场景
尽管 Tahoe 和 Reno 已被更先进的算法(如 CUBIC、BBR)取代,但理解它们对于以下场景仍然至关重要:
| 场景 | 推荐算法 | 原因 |
|---|---|---|
| 低丢包率、低延迟网络 | Reno | 快速恢复能有效利用带宽,算法简单开销小 |
| 高丢包率无线网络 | 不推荐 Reno | 多包丢失导致频繁超时,吞吐量骤降 |
| 历史兼容性测试 | Tahoe | 用于验证旧版 TCP 实现的行为 |
| 教学与协议分析 | 两者皆可 | 作为拥塞控制基础理论的参考实现 |
Tahoe 与 Reno 奠定了 TCP 拥塞控制的 AIMD 原则,后续所有算法都是在这一框架上的优化与扩展。掌握这两个算法的状态机转换和窗口更新规则,是深入理解 Linux 内核网络协议栈拥塞控制子系统的基石。