Files
cs-note/hhs/NETWORK/04-传输层/04-TCP流量控制与拥塞控制.md
T
2026-05-24 11:42:38 +08:00

6.3 KiB
Raw Blame History

tags, create time
tags create time
计算机网络
TCP流量控制
拥塞控制
BBR
Cubic
2026-05-18 02:00

流量控制与拥塞控制

概述

TCP 有两个"刹车"机制:流量控制(防止接收方被淹没)和拥塞控制(防止网络被撑爆)。前者是端到端的,后者是全网协同的。

流量控制(Flow Control)

滑动窗口原理

flowchart LR
    Sender["发送方"] -->|"seq 100..299"| Recv["接收方<br/>rwnd = 300"]
    Recv -->|"ACK=100, window=300"| Sender
    
    Note over Sender:"可用窗口 = min(cwnd, rwnd)"
    Note over Recv:"rwnd = RecvBufSize - UnackedData"
概念 含义
rwnd (Receive Window) 接收方的剩余缓冲区大小,告诉发送方"我还能收多少"
cwnd (Congestion Window) 拥塞窗口,由发送方根据网络状况自行估算
实际可用窗口 min(rwnd, cwnd) — 取两者较小值

零窗口问题

当接收方缓冲区满时,会通告窗口大小为 0:

发送方收到 ACK with window=0 → 停止发送数据 → 进入 persist timer 探测状态

Zero Window Probe (ZWP)

Linux 内核的 ZWP 定时发送一个字节的数据来探测窗口是否恢复:

$ sysctl net.ipv4.tcp_no_metrics_save
net.ipv4.tcp_no_metrics_save = 1  # 避免缓存错误的 RTT 值

[!warning] 死锁场景 如果 ZWP 丢失且对端没有响应,连接会永久僵死。解决方案:应用层实现超时重连。

拥塞控制(Congestion Control)

四种核心算法

flowchart TD
    subgraph "慢启动阶段 Slow Start"
        A["cwnd = 1 MSS"] -->|"每个RTT翻倍(指数增长)"| B[cwnd ≥ ssthresh?]
    end
    
    B -->|"是"| C["进入拥塞避免<br/>(线性增长)"]
    
    subgraph "拥塞避免阶段 Congestion Avoidance"
        C -->|"每个RTT+1 MSS<br/>(线性增长)"| D[检测到丢包? ]
    end
    
    D -->|"是"| E["ssthresh = cwnd / 2<br/>cwnd = 1 (或 2 MSS)"]
    E --> F["回到慢启动<br/>(快速恢复)"]
    F --> G["快速恢复后<br/>进入拥塞避免"]
    
    style A fill:#DDA0DD,color:#000
    style C fill:#FFD700,color:#000
    style E fill:#FF6B6B,color:#fff
    style G fill:#98FB98,color:#000

详细对照表

阶段 cwnd 变化 增长速度 适用场景
慢启动 每 RTT × 2 指数 初始探测、快速恢复后
拥塞避免 每 RTT + 1 MSS 线性 接近容量时的精细调节
快重传 收到 3 个 Dup ACK 立即重传 轻微丢包
快恢复 cwnd = max(cwnd/2, 2MSS) 从减半值开始慢启动 伴随快重传

三种触发事件

事件 操作 ssthresh cwnd
3个重复 ACK 快重传 + 快恢复 cwnd / 2 max(cwnd/2, 2×MSS)
RTO 超时 慢开始 cwnd / 2 1 MSS (Reno/Cubic) / 3 segments (NewReno)
SACK 确认部分缺失 SACK-based fast recovery 同上 同上

Linux 拥塞控制算法对比

可选算法列表

$ cat /proc/sys/net/ipv4/congestion_control
bbr

$ ls /lib/modules/$(uname -r)/kernel/net/ipv4/*_cc.ko*
tcp_cubic.ko      tcp_dctcp.ko      tcp_htcp.ko       tcp_highspeed.ko  tcp_hybla.ko      tcp_illinois.ko   tcp_lp.ko         tcp_reno.ko       tcp_scalable.ko   tcp_vegas.ko      tcp_westwood.ko   tcp_yeah.ko       tcp_bbr.ko

主流算法对比

flowchart LR
    Reno["TCP Reno<br/>• cwnd/2 on drop<br/>• Classic cubic curve"]
    Cubic["TCP Cubic (Linux default)<br/>• Non-linear recovery<br/>• Better for high-BDP links"]
    BBR["Google BBR v2<br/>• Model bottleneck<br/>• Max throughput, low latency<br/>• No loss-based triggering"]
    DCTCP["DCTCP (Datacenter)<br/>• ECN-based<br/>• Near-zero congestion in DC"]
    
    style Reno fill:#DDA0DD,color:#000
    style Cubic fill:#FFD700,color:#000
    style BBR fill:#98FB98,color:#000
    style DCTCP fill:#B0C4DE,color:#000
特性 Reno Cubic (默认) BBR v2 DCTCP
触发方式 丢包/重复ACK 丢包/重复ACK 延迟模型 ECN标记
高带宽利用 ⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐⭐
低延迟 ⭐⭐ ⭐⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐
数据中心 ❌ ✅ ✅ (推荐) ✅ (需ECN支持)
WAN/广域网 ✅ ✅ ✅✅ ❌
Google 生产环境 — — ✅ 全部使用 —
配置复杂度 零 调参选项多 最少 (只需设置目标 bw/rtt) 需交换机支持 ECN

BBR v2 核心思想

BBR 不依赖丢包作为拥堵信号——它直接建立网络管道模型:

BBR 维护三个核心测量值:
┌─────────────┬──────────────┬─────────────┐
│  Bottleneck │  Propagation │  In-flight  │
│  Bandwidth  │  Delay (BDP) │  Data Limit │
│  (最高吞吐)  │  (最低延迟)   │  (当前水量)  │
└─────────────┴──────────────┴─────────────┘

然后动态调整: send_rate ≤ bw AND  in_flight ≤ bdp + buffer
# 切换为 BBR
$ sudo sysctl net.ipv4.tcp_congestion_control=bbr
$ echo 'net.ipv4.tcp_congestion_control=bbr' >> /etc/sysctl.conf

# 验证
$ ss --info
... cubic bbr  ...

Go 中的实践

设置 TCP 选项

import "golang.org/x/net/ipv4"

// 设置 socket 级别的拥塞控制相关参数
conn, _ := net.Dial("tcp", "example.com:80")
c := ipv4.NewConn(conn.RawConn())
c.SetTrafficClass(0)           // DSCP/ECN
c.SetNoDelay(false)            // Nagle 算法关闭 → 小包立刻发
// http.Transport 的连接管理
transport := &http.Transport{
    MaxIdleConns:        100,
    MaxIdleConnsPerHost: 10,
    IdleConnTimeout:     90 * time.Second,
}

// 自定义 DialContext 以启用 keepalive
dialer := &net.Dialer{
    Timeout:   30 * time.Second,
    KeepAlive: 30 * time.Second, // 替代内核默认的 7200s
}

关联笔记