1. 拥塞控制概述
1.1 什么是拥塞
拥塞是指网络中出现大量数据包堆积,导致网络性能下降的现象。
**拥塞表现**:
-
数据包延迟增加
-
丢包率上升
-
网络吞吐量下降
-
甚至造成网络瘫痪
1.2 拥塞控制 vs 流量控制
**流量控制**:
-
端到端的控制
-
防止发送方淹没接收方
-
基于接收方缓冲区大小(rwnd)
**拥塞控制**:
-
网络层面的控制
-
防止发送方过多数据注入网络
-
基于网络拥塞程度(cwnd)
1.3 拥塞窗口(cwnd)
```
发送窗口 = min(rwnd, cwnd)
-
rwnd:接收窗口,接收方告知
-
cwnd:拥塞窗口,发送方自己控制
```
2. 慢启动与拥塞避免
2.1 慢启动
**原理**:
-
连接开始时,cwnd初始化为一个较小的值
-
每收到一个ACK,cwnd增加一个MSS
-
指数增长,快速探测网络容量
**公式**:
```
每收到一个ACK:cwnd = cwnd + MSS
等同于每RTT:cwnd = cwnd * 2
```
**慢启动阈值(ssthresh)**:
-
当cwnd达到ssthresh时,进入拥塞避免
-
典型初始值:65535字节
**实例**:
```
初始:cwnd=1 MSS, ssthresh=64 MSS
第1个RTT:cwnd=2 MSS
第2个RTT:cwnd=4 MSS
第3个RTT:cwnd=8 MSS
第4个RTT:cwnd=16 MSS
...
直到cwnd达到ssthresh
```
2.2 拥塞避免
**原理**:
-
cwnd达到ssthresh后进入
-
每收到一个ACK,cwnd增加(MSS * MSS / cwnd)
-
线性增长,缓慢探测网络容量
**公式**:
```
每收到一个ACK:cwnd = cwnd + MSS * (MSS / cwnd)
等同于每RTT:cwnd = cwnd + MSS
```
**实例**:
```
场景:cwnd=64 MSS, MSS=1 KB
每收到一个ACK:
cwnd = cwnd + 1 * (1 / 64) = cwnd + 1/64 KB
每RTT(假设64个ACK):
cwnd = 64 + 64 * (1/64) = 65 KB
```
2.3 完整拥塞控制过程
```
连接建立:
cwnd = 1 MSS, ssthresh = 65535
慢启动阶段:
cwnd指数增长,直到达到ssthresh
拥塞避免阶段:
cwnd线性增长
检测到丢包:
ssthresh = cwnd / 2
cwnd = 1 MSS
重新开始慢启动
```
3. 快速重传与快速恢复
3.1 快速重传
**触发条件**:
-
收到3个或更多重复ACK
-
说明有数据包丢失,但网络仍可通
**原理**:
-
不等待超时重传
-
立即重传丢失的数据包
**流程**:
```
发送方发送1,2,3,4,5
接收方只收到1,2,3,5(4丢失)
接收方发送3个ACK(3)
发送方收到第1个ACK(3):可能是乱序,不重传
发送方收到第2个ACK(3):可能是乱序,不重传
发送方收到第3个ACK(3):确认4丢失,立即重传
```
3.2 快速恢复
**原理**:
-
快速重传后不进入完全慢启动
-
轻微调整后继续传输
**传统快速恢复**:
```
-
收到3个重复ACK时
-
ssthresh = cwnd / 2
-
cwnd = ssthresh + 3 * MSS
-
重传丢失数据包
-
每收到一个重复ACK:cwnd = cwnd + MSS
-
收到新数据的ACK时:cwnd = ssthresh
-
进入拥塞避免
```
**实例**:
```
场景:cwnd=100 MSS,检测到丢包
步骤1:ssthresh = 50 MSS
步骤2:cwnd = 50 + 3 = 53 MSS
步骤3:重传丢失数据包
步骤4:收到重复ACK,cwnd = 54 MSS
步骤5:收到新ACK,cwnd = 50 MSS
步骤6:进入拥塞避免
```
4. CUBIC与BBR算法区别
4.1 CUBIC算法
**特点**:
-
Linux默认TCP拥塞控制算法
-
基于丢包的拥塞控制
-
使用三次多项式函数调整窗口
**调整公式**:
```
W(t) = C * (t - K)^3 + Wmax
-
C:缩放因子
-
t:当前时间
-
K:窗口达到Wmax的时间
-
Wmax:上次丢包前的窗口大小
```
**优势**:
-
高带宽高延迟网络表现好
-
收敛速度快
-
公平性好
**劣势**:
-
激进,容易造成bufferbloat
-
对丢包敏感
4.2 BBR算法
**特点**:
-
Google开发
-
基于模型的拥塞控制
-
不依赖丢包检测
**核心原理**:
-
估计可用带宽
-
估计往返延迟
-
调整发送速率
**BBR状态**:
```
-
Startup(启动):类似于慢启动,快速探测带宽
-
Drain(排空):排空瓶颈缓冲区
-
ProbeBW(带宽探测):周期性探测带宽
-
ProbeRTT(延迟探测):周期性探测延迟
```
**优势**:
-
低延迟
-
不受bufferbloat影响
-
高带宽高延迟网络表现好
**劣势**:
-
公平性问题
-
需要较长预热时间
4.3 算法对比
| 特性 | CUBIC | BBR |
|------|-------|-----|
| 拥塞信号 | 丢包 | 延迟/带宽估计 |
| 缓冲区使用 | 积极填充 | 最小化填充 |
| 延迟表现 | 高延迟(bufferbloat) | 低延迟 |
| 丢包敏感度 | 高 | 低 |
| 适用场景 | 短距离网络 | 长距离/高带宽网络 |
| 收敛速度 | 快 | 较慢 |
5. 丢包与延迟对拥塞窗口的影响
5.1 丢包对拥塞窗口的影响
**超时丢包**:
```
影响最大
ssthresh = cwnd / 2
cwnd = 1 MSS
完全重新开始慢启动
```
**3个重复ACK丢包**:
```
影响较小
ssthresh = cwnd / 2
cwnd = ssthresh + 3 * MSS
进入快速恢复
```
**丢包率对吞吐量的影响**:
```
丢包率 = 0.1%:吞吐量影响较小
丢包率 = 1%:吞吐量下降明显
丢包率 = 10%:吞吐量严重下降
```
5.2 延迟对拥塞窗口的影响
**高延迟的影响**:
```
RTT增加,吞吐量下降
带宽-延迟积 = BD = 带宽 * RTT
需要更大的窗口才能利用带宽
```
**实例**:
```
场景:100Mbps带宽,RTT=100ms
带宽-延迟积:
BD = 100Mbps * 100ms = 12.5Mb = 1.25MB
如果cwnd < 1.25MB:
无法充分利用带宽
如果cwnd = 1.25MB:
可以充分利用带宽
```
5.3 bufferbloat问题
**问题描述**:
-
中间设备(路由器、交换机)缓冲区过大
-
数据包在缓冲区排队
-
导致延迟增加,但不会丢包
-
发送方认为网络正常,继续增加窗口
**影响**:
-
延迟大幅增加
-
交互应用体验变差
-
BBR等新算法可以缓解
5.4 实例:拥塞控制全过程
```
场景:1Gbps网络,RTT=20ms,MSS=1KB
步骤1:连接建立
cwnd = 1 MSS = 1 KB
ssthresh = 65535 KB
步骤2:慢启动
cwnd指数增长:1→2→4→8→16→32→64 KB
8个RTT后达到ssthresh
步骤3:拥塞避免
cwnd线性增长:64→65→66→67 KB
步骤4:检测到3个重复ACK
ssthresh = 67 / 2 = 33 KB
cwnd = 33 + 3 = 36 KB
进入快速恢复
步骤5:收到新ACK
cwnd = ssthresh = 33 KB
进入拥塞避免
```
6. 拥塞控制优化建议
6.1 选择合适的算法
```
高带宽高延迟:BBR
短距离网络:CUBIC
混合场景:依情况选择
```
6.2 参数调优
```
tcp_congestion_control:设置拥塞算法
net.ipv4.tcp_ssthresh:调整ssthresh初始值
net.ipv4.tcp_window_scaling:启用窗口扩展
```
6.3 监控与诊断
```
监控cwnd变化
分析丢包率
测量延迟变化
识别拥塞信号
```
7. 总结
TCP拥塞控制是保证网络稳定运行的关键机制:
-
**慢启动**:快速探测网络容量,指数增长cwnd
-
**拥塞避免**:达到ssthresh后,线性增长cwnd
-
**快速重传**:收到3个重复ACK时立即重传
-
**快速恢复**:轻微调整后继续传输
-
**CUBIC vs BBR**:基于丢包vs基于模型,各有优劣
-
**丢包和延迟**:都会影响cwnd调整策略
理解拥塞控制对于网络优化、性能调优和问题排查至关重要。在实际应用中,需要根据网络特性选择合适的拥塞控制算法和参数配置。