第一百一十七章:拥塞
问道期涉及内核源码:
一
林小源走在一条宽阔的水道上,水道两侧是高耸的石壁。水面上漂浮着无数光点——每一个都是一个数据包,争先恐后地向前涌去。
"慢点!慢点!"一个尖锐的声音从前方传来。
林小源看见一个瘦削的青年站在水道中央,双臂张开,像是要拦住所有的光点。他穿着一件灰色的袍子,上面绣着一条指数增长的曲线,从底部急速攀升。
"你是谁?"
"慢启动。"青年说,声音有些急促,"我在控制流量——你看看这水道,再不减速就要溢了。"
林小源向前看去——水道越来越窄,前方有一个巨大的闸门,光点在闸门前挤成一团,有些被挤碎了,化为虚无。
"那些碎掉的是……"
"丢包。"慢启动的脸色难看,"太多数据包同时涌向网络,路由器的缓冲区溢出,数据包就被丢弃了。这就是拥塞。"
林小源看着那些消散的光点,感到一阵心痛:"那怎么办?"
"所以我在。"慢启动深吸一口气,"我不让发送方一开始就全速发送——从一个 MSS 开始,每收到一个 ACK 就翻倍。指数增长,但起步很慢。像探路一样——先伸出一只脚,确认安全了再迈下一步。"
二
水道渐渐变宽,闸门前的拥堵缓解了。林小源看到一个沉稳的中年人从旁边走来,他的步伐均匀而缓慢,每一步都踩在固定的节拍上。
"拥塞避免。"慢启动介绍道。
"你在指数增长阶段表现不错。"拥塞避免对慢启动说,"但到了 ssthresh 就该换我了——线性增长,每 RTT 加一个 MSS。"
林小源看着两人交接——慢启动退后,拥塞避免走上前,步伐立刻变得平稳。"为什么不一直用指数增长?快多了。"
拥塞避免回头看了他一眼:"太快会出事。cwnd 越大,网络承受的压力越大。指数增长只能用来探测——到了阈值就该收敛,用线性增长小心翼翼地逼近极限。"
"那如果还是拥塞了呢?"
"那就要快重传和快恢复。"拥塞避免指向远处。
林小源看到另一个区域——一个敏捷的少年正在快速移动,手中握着三面小旗。每当他收到三个重复的 ACK,就立刻重传丢失的包,不等超时。
"三个重复 ACK 说明后面的包到了,中间那个丢了。"少年喊道,"不等超时,立刻重传!然后把 cwnd 减半,进入拥塞避免——这就是快恢复。"
三
林小源在水道的尽头看到了一块巨大的石碑,上面刻着三种名字:Reno、CUBIC、BBR。
"这是拥塞控制的三大门派。"一个苍老的声音说。
林小源转身,看到一个白发老者坐在石碑旁,手中握着一根拐杖,拐杖上刻着一条三次函数曲线。
"我是 CUBIC。"老者说,"Reno 是老前辈,用线性增长。我用三次函数——恢复更快,更适合高带宽网络。"
"BBR 呢?"林小源问。
老者指向远方——那里有一个奇怪的身影,他不像其他人那样盯着丢包,而是不停地测量着什么。"BBR 不看丢包,它看带宽和延迟。传统拥塞控制认为丢包就是拥塞——但无线网络丢包不一定是拥塞,可能是干扰。BBR 测量实际可用带宽和最小 RTT,更准确地判断网络状态。"
"那 Reno 呢?"
"经典,但简单。"老者站起身,"快重传加快恢复,遇到超时就回到慢启动。在高带宽长延迟的网络中表现不好——恢复太慢。但它是基础,后来的算法都是在它之上改进的。"
林小源看着石碑上的三个名字,忽然明白了——拥塞控制不是一种算法,而是一个不断演化的领域。每种算法都在寻找同一个平衡点:既不浪费带宽,又不造成拥塞。
/*
* 拥塞控制的核心算法:
*
* 1. 慢启动 (Slow Start)
* cwnd 从 1 MSS 开始
* 每收到一个 ACK,cwnd++
* cwnd 指数增长
*
* 2. 拥塞避免 (Congestion Avoidance)
* cwnd 达到 ssthresh 后
* cwnd 线性增长
*
* 3. 快重传 (Fast Retransmit)
* 收到 3 个重复 ACK
* 立即重传丢失的包
*
* 4. 快恢复 (Fast Recovery)
* ssthresh = cwnd / 2
* cwnd = ssthresh
* 进入拥塞避免
*
* 拥塞检测:
* 超时: cwnd = 1, 慢启动
* 重复 ACK: 快重传 + 快恢复
*/
printf("=== TCP 拥塞控制 — 避免网络过载 ===\n\n");
printf("拥塞控制的核心算法:\n\n");
printf("1. 慢启动:\n");
printf(" cwnd = 1 MSS\n");
printf(" 每个 ACK: cwnd++\n");
printf(" cwnd: 1 → 2 → 4 → 8 → 16...\n\n");
printf("2. 拥塞避免:\n");
printf(" cwnd >= ssthresh\n");
printf(" 每个 RTT: cwnd++\n");
printf(" cwnd 线性增长\n\n");
printf("3. 快重传:\n");
printf(" 收到 3 个重复 ACK\n");
printf(" 立即重传\n\n");
printf("4. 快恢复:\n");
printf(" ssthresh = cwnd / 2\n");
printf(" cwnd = ssthresh\n");
printf(" 进入拥塞避免\n\n");
printf("--- 拥塞窗口变化 ---\n");
printf("cwnd\n");
printf(" │ /\\ 拥塞\n");
printf(" │ / \\ ↓\n");
printf(" │ / \\ 快恢复\n");
printf(" │ / \\ /\n");
printf(" │/ 慢启动 \\/\n");
printf(" └──────────── time\n\n");
printf("--- 拥塞算法 ---\n");
printf("Reno:\n");
printf(" 经典算法\n");
printf(" 快重传 + 快恢复\n\n");
printf("CUBIC:\n");
printf(" 默认算法\n");
printf(" 三次函数增长\n\n");
printf("BBR:\n");
printf(" Google 提出\n");
printf(" 基于带宽和延迟\n");#include <stdio.h>
/*
* 拥塞控制的核心算法:
*
* 1. 慢启动 (Slow Start)
* cwnd 从 1 MSS 开始
* 每收到一个 ACK,cwnd++
* cwnd 指数增长
*
* 2. 拥塞避免 (Congestion Avoidance)
* cwnd 达到 ssthresh 后
* cwnd 线性增长
*
* 3. 快重传 (Fast Retransmit)
* 收到 3 个重复 ACK
* 立即重传丢失的包
*
* 4. 快恢复 (Fast Recovery)
* ssthresh = cwnd / 2
* cwnd = ssthresh
* 进入拥塞避免
*
* 拥塞检测:
* 超时: cwnd = 1, 慢启动
* 重复 ACK: 快重传 + 快恢复
*/
int main() {
printf("=== TCP 拥塞控制 — 避免网络过载 ===\n\n");
printf("拥塞控制的核心算法:\n\n");
printf("1. 慢启动:\n");
printf(" cwnd = 1 MSS\n");
printf(" 每个 ACK: cwnd++\n");
printf(" cwnd: 1 → 2 → 4 → 8 → 16...\n\n");
printf("2. 拥塞避免:\n");
printf(" cwnd >= ssthresh\n");
printf(" 每个 RTT: cwnd++\n");
printf(" cwnd 线性增长\n\n");
printf("3. 快重传:\n");
printf(" 收到 3 个重复 ACK\n");
printf(" 立即重传\n\n");
printf("4. 快恢复:\n");
printf(" ssthresh = cwnd / 2\n");
printf(" cwnd = ssthresh\n");
printf(" 进入拥塞避免\n\n");
printf("--- 拥塞窗口变化 ---\n");
printf("cwnd\n");
printf(" │ /\\ 拥塞\n");
printf(" │ / \\ ↓\n");
printf(" │ / \\ 快恢复\n");
printf(" │ / \\ /\n");
printf(" │/ 慢启动 \\/\n");
printf(" └──────────── time\n\n");
printf("--- 拥塞算法 ---\n");
printf("Reno:\n");
printf(" 经典算法\n");
printf(" 快重传 + 快恢复\n\n");
printf("CUBIC:\n");
printf(" 默认算法\n");
printf(" 三次函数增长\n\n");
printf("BBR:\n");
printf(" Google 提出\n");
printf(" 基于带宽和延迟\n");
return 0;
}道藏笔记
内核启示
TCP 拥塞控制避免网络过载。
拥塞控制算法:
- 慢启动 — cwnd 指数增长
- 拥塞避免 — cwnd 线性增长
- 快重传 — 收到 3 个重复 ACK 立即重传
- 快恢复 — cwnd 减半后进入拥塞避免
拥塞算法:
- Reno — 经典算法
- CUBIC — 默认算法,三次函数增长
- BBR — 基于带宽和延迟
拥塞控制是"探测"——让 TCP 适应网络条件。
拥塞控制之试
拥塞控制一章讨论慢启动、拥塞窗口和退让,主要服务于哪种传输协议?