飞翔飞翔
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
  • 数据库

    • SQL教程
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON教程
  • 工具

    • Markdown指南
  • Git

    • GitFlow
  • Quartz

    • Quartz教程
  • Java

    • Java设计模式
  • 缓存

    • Redis教程
联系
阿里云
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
  • 数据库

    • SQL教程
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON教程
  • 工具

    • Markdown指南
  • Git

    • GitFlow
  • Quartz

    • Quartz教程
  • Java

    • Java设计模式
  • 缓存

    • Redis教程
联系
阿里云
  • 学习路径
  • IP协议

    • 认识 IP 协议与网络层定位
    • IPv4 编址体系与分类地址
    • CIDR 与子网划分实战
    • IPv6 编址体系
    • ARP 协议详解
    • NDP 协议详解
    • IGMP 与 MLD 组播侦听发现
    • VRRP 与网关冗余
    • IPv4 数据报首部解析
    • IPv6 数据报与扩展首部
    • IPsec 安全扩展
    • 分片、MTU 与路径发现
    • ICMP 与 Traceroute 原理
    • 路由基础与转发流水线
    • 动态路由协议
    • NAT 网络地址转换
    • DHCP 与自动配置
    • Wireshark 与命令行工具
    • IPv6 过渡技术
    • IP 协议栈排障与攻击防御
  • TCP协议

    • 认识 TCP 协议与传输层定位
    • TCP 报文段首部解析
    • 三次握手与连接建立
    • 四次挥手与连接释放
    • TCP 有限状态机
    • 序列号与确认机制
    • 超时重传与 RTO 计算
    • 滑动窗口与流量控制
    • 拥塞控制基础
    • 现代拥塞控制算法
    • TCP 选项与扩展
    • TCP 性能调优与内核参数
    • Nagle 算法与糊涂窗口综合征
    • TCP 定时器与 Keep-Alive 机制
    • TCP 安全与攻击防御
    • TCP 与上层/下层交互
    • TCP 综合实践与排障

拥塞控制基础

流量控制 vs 拥塞控制

维度流量控制拥塞控制
问题接收方处理不过来网络中间设备(路由器)处理不过来
范围端到端全局性(影响整个网络)
信号接收窗口 rwnd丢包 / 延迟 / ECN
目标保护接收方保护网络

生活例子:

  • 流量控制 = 餐厅后厨出菜速度匹配前厅服务员的端菜能力
  • 拥塞控制 = 整条美食街的餐厅 collectively 控制客流,防止街道堵塞

拥塞窗口 cwnd 与慢启动阈值 ssthresh

TCP 发送方维护两个关键变量:

  • cwnd(Congestion Window):拥塞窗口,发送方根据网络状况自我限制的发送量
  • ssthresh(Slow Start Threshold):慢启动阈值,cwnd 超过此值后转入拥塞避免

实际发送窗口:

发送窗口 = min(rwnd, cwnd)

经典拥塞控制四阶段(RFC 5681)

1. 慢启动(Slow Start)

连接建立或超时恢复后:

  • cwnd 初始值 = 1~2 MSS(RFC 5681 建议 2~4 MSS,现代 Linux 默认 10 MSS)
  • 每收到一个 ACK,cwnd += 1 MSS
  • 每 RTT 翻倍,呈指数增长
RTT 1: cwnd = 1 MSS, 发送 1 段
RTT 2: cwnd = 2 MSS, 发送 2 段
RTT 3: cwnd = 4 MSS, 发送 4 段
RTT 4: cwnd = 8 MSS, 发送 8 段
...
直到 cwnd ≥ ssthresh

为什么叫"慢"启动? 相对"一次性发满窗口"而言,它从 1 开始慢慢探测网络容量。实际上指数增长很快。

2. 拥塞避免(Congestion Avoidance)

当 cwnd ≥ ssthresh 时:

  • 每收到一个 ACK,cwnd += 1/cwnd 个 MSS
  • 每 RTT 增加 1 MSS,呈线性增长
RTT N: cwnd = 16 MSS
RTT N+1: cwnd = 17 MSS
RTT N+2: cwnd = 18 MSS
...

这种保守增长探测网络容量边界,避免突然注入大量数据导致拥塞。

3. 快重传(Fast Retransmit)

收到 3 个重复 ACK 时:

  • 立即重传丢失段,不等待 RTO 超时
  • 这通常意味着网络仅发生轻度拥塞(个别段丢失)

4. 快恢复(Fast Recovery)

快重传后,不将 cwnd 降至 1(避免慢启动的剧烈震荡):

ssthresh = cwnd / 2
cwnd = ssthresh + 3 MSS

然后每收到一个重复 ACK,cwnd += 1 MSS;收到新数据的 ACK 后,cwnd = ssthresh,进入拥塞避免。

为什么 +3 MSS? 因为收到 3 个重复 ACK 说明网络中还有 3 个段在传输,可以多发 3 个段保持管道满载。

拥塞信号与响应总结

事件信号含义ssthreshcwnd后续阶段
超时严重拥塞cwnd/21 MSS慢启动
3 个重复 ACK轻度拥塞cwnd/2ssthresh+3快恢复 → 拥塞避免

AIMD 的公平性

从控制理论角度,TCP 拥塞控制是**AIMD(Additive Increase Multiplicative Decrease)**算法:

  • 加性增加:线性增长探测带宽
  • 乘性减少:丢包时窗口减半

Chiu-Jain 公平性证明:多条连接共享瓶颈链路时,AIMD 会收敛到每条连接获得相等的带宽份额。

ECN:显式拥塞通知(RFC 3168)

传统 TCP 通过丢包推断拥塞,但丢包本身浪费带宽。ECN 让路由器在即将拥塞时主动标记而非丢弃:

  • IP 首部 DSCP 字段中的两位:ECT(ECN-Capable Transport)和 CE(Congestion Experienced)
  • TCP 首部保留两位:ECE(ECN-Echo)和 CWR(Congestion Window Reduced)
  • 路由器标记 CE=1 而非丢弃 → 接收方回显 ECE=1 → 发送方像收到 dup ACK 一样减半 cwnd

优势:无损拥塞通知,避免丢包重传开销,对实时应用友好。

本篇小结

  • 拥塞控制保护网络,流量控制保护接收方
  • 核心变量:cwnd(拥塞窗口)、ssthresh(慢启动阈值)
  • 四阶段:慢启动(指数)→ 拥塞避免(线性)→ 快重传 → 快恢复
  • 超时 = 严重拥塞:cwnd=1, ssthresh=cwnd/2
  • 3 个 dup ACK = 轻度拥塞:cwnd=ssthresh+3, ssthresh=cwnd/2
  • AIMD 算法在数学上可证明收敛到公平带宽分配
  • ECN:路由器主动标记拥塞,避免丢包

动手实践

  1. 查看 Linux 拥塞控制算法:

    sysctl net.ipv4.tcp_congestion_control
    sysctl net.ipv4.tcp_available_congestion_control
    
  2. 切换算法并测试:

    sudo sysctl net.ipv4.tcp_congestion_control=cubic
    # 或 reno, bbr
    
  3. 查看 ECN 设置:

    sysctl net.ipv4.tcp_ecn
    
  4. 用 iperf3 测试不同算法:

    # 服务端
    iperf3 -s
    # 客户端
    iperf3 -c <server_ip> -t 30
    
  5. 思考:为什么慢启动是指数增长而不是线性?如果 cwnd 每 RTT 只加 1,连接建立初期需要多久才能达到可用带宽?

上一页
滑动窗口与流量控制
下一页
现代拥塞控制算法