【计算基础|网络11】TCP(下):拥塞控制与参数优化
前两篇解决了"怎么建连"和"怎么不丢数据"。这一篇回答最后一个问题:全网都在抢带宽时,发送方怎么知道自己该发多快? 上篇说过实际发送窗口 = min(rwnd, cwnd),rwnd 是接收方的事,这一篇专讲 cwnd(拥塞窗口) 怎么来、怎么调,以及生产服务器那一堆 TCP 内核参数到底改哪个、怎么改。
环境:内核参数调优以 Linux 为主(
man 7 tcp、sysctl),macOS 等价参数括注。本机 macOS 实测缓冲区net.inet.tcp.sendspace/recvspace = 131072。
一、先厘清:流量控制和拥塞控制不是一回事
这是面试高频送分题,先把边界划清楚:
| 流量控制 | 拥塞控制 | |
|---|---|---|
| 管谁 | 接收方别被撑爆 | 整个网络别被压垮 |
| 信号 | 接收方通过首部窗口字段通告 rwnd | 发送方自己根据丢包/延迟推断 cwnd |
| 作用范围 | 点对点 | 全局 |
| 本篇 | 上篇讲过 | 本篇主角 |
一句话:接收方肚子小就限流是流量控制;路上堵车了就收油门是拥塞控制。 发送方实际能发多少,取决于两个里更小的那个。
二、经典四件套:慢启动、拥塞避免、快重传、快恢复
经典 TCP Reno 用四条算法配合,目标是:既充分利用带宽,又在拥塞时及时收手。
2.1 慢启动(Slow Start):指数增长
连接刚建好,发送方不知道网络能扛多少,于是从小开始、指数试探:
- cwnd 从 1 个 MSS 开始。
- 每收到一个 ACK,cwnd 加 1 MSS。
- 因为一个 RTT 内会收到很多 ACK,相当于每过一个 RTT,cwnd 翻倍(1→2→4→8→...)。
虽然叫"慢",其实增长是指数 的,很快就会撞到网络瓶颈。撞到一个阈值 ssthresh(慢启动阈值) 后,转入拥塞避免。
2.2 拥塞避免(Congestion Avoidance):线性增长
cwnd 超过 ssthresh 后,改成每过一个 RTT 只加 1 个 MSS(线性增长)。意思是:网络已经试探出大概能扛多少了,别再翻倍猛冲,小心地慢慢往上加,逼近带宽极限。
2.3 快重传(Fast Retransmit):3 个重复 ACK
上篇讲过:收到 3 个重复 ACK 就立刻重传,不等超时。这里从拥塞控制视角再看一眼------它是"轻微拥塞"的信号。
2.4 快恢复(Fast Recovery):别从头再来
快重传触发后,不回到慢启动(那也太惨了),而是:
ssthresh = cwnd / 2
cwnd = ssthresh (直接砍半,然后进入拥塞避免线性增长)
意思:既然只是个别包丢了、网络还通着,那就把油门松一半,继续试探。
2.5 两种丢包,两种反应
🔴 拥塞控制靠丢包信号判断网络状态,丢包分两种:
| 触发 | 网络状态 | 反应 |
|---|---|---|
| 超时(RTO 到了还没 ACK) | 严重拥塞,网络堵死了 | ssthresh = cwnd/2,cwnd = 1,重新慢启动 |
| 3 个重复 ACK | 轻微拥塞,个别包丢但还通着 | ssthresh = cwnd/2,cwnd = ssthresh,进快恢复 |
2.6 cwnd 随时间变化的经典曲线
渲染错误: Mermaid 渲染失败: Parse error on line 5: ..."发生丢包"} D -->|超时(严重)| E["ssthresh=cw ----------------------^ Expecting 'SQE', 'DOUBLECIRCLEEND', 'PE', '-)', 'STADIUMEND', 'SUBROUTINEEND', 'PIPE', 'CYLINDEREND', 'DIAMOND_STOP', 'TAGEND', 'TRAPEND', 'INVTRAPEND', 'UNICODE_TEXT', 'TEXT', 'TAGSTART', got 'PS'
对应的波形(慢启动指数爬到 ssthresh → 拥塞避免线性爬 → 丢包砍半 → 再线性爬 → 又砍半......锯齿):
#mermaid-svg-fh0F9sfb2C4zY0Dj{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-fh0F9sfb2C4zY0Dj .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-fh0F9sfb2C4zY0Dj .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-fh0F9sfb2C4zY0Dj .error-icon{fill:#552222;}#mermaid-svg-fh0F9sfb2C4zY0Dj .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-fh0F9sfb2C4zY0Dj .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-fh0F9sfb2C4zY0Dj .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-fh0F9sfb2C4zY0Dj .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-fh0F9sfb2C4zY0Dj .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-fh0F9sfb2C4zY0Dj .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-fh0F9sfb2C4zY0Dj .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-fh0F9sfb2C4zY0Dj .marker{fill:#333333;stroke:#333333;}#mermaid-svg-fh0F9sfb2C4zY0Dj .marker.cross{stroke:#333333;}#mermaid-svg-fh0F9sfb2C4zY0Dj svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-fh0F9sfb2C4zY0Dj p{margin:0;}#mermaid-svg-fh0F9sfb2C4zY0Dj :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} cwnd 随 RTT 变化(Reno) 123456789101112 RTT 次数 4035302520151050 cwnd (MSS)
2.7 拥塞控制状态机
#mermaid-svg-y2gOHN8gWR4cquWe{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-y2gOHN8gWR4cquWe .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-y2gOHN8gWR4cquWe .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-y2gOHN8gWR4cquWe .error-icon{fill:#552222;}#mermaid-svg-y2gOHN8gWR4cquWe .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-y2gOHN8gWR4cquWe .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-y2gOHN8gWR4cquWe .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-y2gOHN8gWR4cquWe .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-y2gOHN8gWR4cquWe .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-y2gOHN8gWR4cquWe .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-y2gOHN8gWR4cquWe .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-y2gOHN8gWR4cquWe .marker{fill:#333333;stroke:#333333;}#mermaid-svg-y2gOHN8gWR4cquWe .marker.cross{stroke:#333333;}#mermaid-svg-y2gOHN8gWR4cquWe svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-y2gOHN8gWR4cquWe p{margin:0;}#mermaid-svg-y2gOHN8gWR4cquWe defs #statediagram-barbEnd{fill:#333333;stroke:#333333;}#mermaid-svg-y2gOHN8gWR4cquWe g.stateGroup text{fill:#9370DB;stroke:none;font-size:10px;}#mermaid-svg-y2gOHN8gWR4cquWe g.stateGroup text{fill:#333;stroke:none;font-size:10px;}#mermaid-svg-y2gOHN8gWR4cquWe g.stateGroup .state-title{font-weight:bolder;fill:#131300;}#mermaid-svg-y2gOHN8gWR4cquWe g.stateGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-y2gOHN8gWR4cquWe g.stateGroup line{stroke:#333333;stroke-width:1;}#mermaid-svg-y2gOHN8gWR4cquWe .transition{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-y2gOHN8gWR4cquWe .stateGroup .composit{fill:white;border-bottom:1px;}#mermaid-svg-y2gOHN8gWR4cquWe .stateGroup .alt-composit{fill:#e0e0e0;border-bottom:1px;}#mermaid-svg-y2gOHN8gWR4cquWe .state-note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-y2gOHN8gWR4cquWe .state-note text{fill:black;stroke:none;font-size:10px;}#mermaid-svg-y2gOHN8gWR4cquWe .stateLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-y2gOHN8gWR4cquWe .edgeLabel .label rect{fill:#ECECFF;opacity:0.5;}#mermaid-svg-y2gOHN8gWR4cquWe .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-y2gOHN8gWR4cquWe .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-y2gOHN8gWR4cquWe .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-y2gOHN8gWR4cquWe .edgeLabel .label text{fill:#333;}#mermaid-svg-y2gOHN8gWR4cquWe .label div .edgeLabel{color:#333;}#mermaid-svg-y2gOHN8gWR4cquWe .stateLabel text{fill:#131300;font-size:10px;font-weight:bold;}#mermaid-svg-y2gOHN8gWR4cquWe .node circle.state-start{fill:#333333;stroke:#333333;}#mermaid-svg-y2gOHN8gWR4cquWe .node .fork-join{fill:#333333;stroke:#333333;}#mermaid-svg-y2gOHN8gWR4cquWe .node circle.state-end{fill:#9370DB;stroke:white;stroke-width:1.5;}#mermaid-svg-y2gOHN8gWR4cquWe .end-state-inner{fill:white;stroke-width:1.5;}#mermaid-svg-y2gOHN8gWR4cquWe .node rect{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-y2gOHN8gWR4cquWe .node polygon{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-y2gOHN8gWR4cquWe #statediagram-barbEnd{fill:#333333;}#mermaid-svg-y2gOHN8gWR4cquWe .statediagram-cluster rect{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-y2gOHN8gWR4cquWe .cluster-label,#mermaid-svg-y2gOHN8gWR4cquWe .nodeLabel{color:#131300;}#mermaid-svg-y2gOHN8gWR4cquWe .statediagram-cluster rect.outer{rx:5px;ry:5px;}#mermaid-svg-y2gOHN8gWR4cquWe .statediagram-state .divider{stroke:#9370DB;}#mermaid-svg-y2gOHN8gWR4cquWe .statediagram-state .title-state{rx:5px;ry:5px;}#mermaid-svg-y2gOHN8gWR4cquWe .statediagram-cluster.statediagram-cluster .inner{fill:white;}#mermaid-svg-y2gOHN8gWR4cquWe .statediagram-cluster.statediagram-cluster-alt .inner{fill:#f0f0f0;}#mermaid-svg-y2gOHN8gWR4cquWe .statediagram-cluster .inner{rx:0;ry:0;}#mermaid-svg-y2gOHN8gWR4cquWe .statediagram-state rect.basic{rx:5px;ry:5px;}#mermaid-svg-y2gOHN8gWR4cquWe .statediagram-state rect.divider{stroke-dasharray:10,10;fill:#f0f0f0;}#mermaid-svg-y2gOHN8gWR4cquWe .note-edge{stroke-dasharray:5;}#mermaid-svg-y2gOHN8gWR4cquWe .statediagram-note rect{fill:#fff5ad;stroke:#aaaa33;stroke-width:1px;rx:0;ry:0;}#mermaid-svg-y2gOHN8gWR4cquWe .statediagram-note rect{fill:#fff5ad;stroke:#aaaa33;stroke-width:1px;rx:0;ry:0;}#mermaid-svg-y2gOHN8gWR4cquWe .statediagram-note text{fill:black;}#mermaid-svg-y2gOHN8gWR4cquWe .statediagram-note .nodeLabel{color:black;}#mermaid-svg-y2gOHN8gWR4cquWe .statediagram .edgeLabel{color:red;}#mermaid-svg-y2gOHN8gWR4cquWe #dependencyStart,#mermaid-svg-y2gOHN8gWR4cquWe #dependencyEnd{fill:#333333;stroke:#333333;stroke-width:1;}#mermaid-svg-y2gOHN8gWR4cquWe .statediagramTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-y2gOHN8gWR4cquWe :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} cwnd >= ssthresh
超时, cwnd=1
3个重复ACK
收到新ACK
超时, cwnd=1
SlowStart
CongestionAvoidance
FastRecovery
三、现代拥塞控制算法:别再只背 Reno
Reno 是教科书起点,但现在 Linux 默认不是 Reno ,而是 Cubic ;Google 还搞了 BBR。
| 算法 | 提出者 | 核心思路 | 适用场景 |
|---|---|---|---|
| Reno | 经典 | 慢启动+拥塞避免+快恢复,靠丢包调 | 教科书基线 |
| New Reno | --- | 改进快恢复,一次 RTT 丢多个包也能逐个恢复 | Reno 的小修小补 |
| Cubic | Linux | 凹函数增长:丢包后快速爬到上次窗口,再小心试探,更公平 | Linux 2.6.19 起的默认算法 |
| BBR | 不靠丢包,直接测瓶颈带宽和 RTT,按模型发包 | 高带宽长延迟(BDP 大)网络表现好 |
🔴 两个关键认知:
- Cubic 是 Linux 现在的默认,你面试/调优时说"默认 Reno"就过时了。
- BBR 不把丢包当拥塞信号------它主动探测瓶颈带宽(Bottleneck Bandwidth)和往返时间(RTT),用模型算出该发多少。在"带宽高、延迟大、但偶尔随机丢包"的长肥网络里(比如跨国链路、卫星),传统算法因为把随机丢包当拥塞、一直降速,BBR 能跑满很多。
查看当前用哪个(Linux):
bash
$ sysctl net.ipv4.tcp_congestion_control
net.ipv4.tcp_congestion_control = cubic
$ sysctl net.ipv4.tcp_available_congestion_control
net.ipv4.tcp_available_congestion_control = reno cubic
想用 BBR(需要内核支持并加载 tcp_bbr 模块):
bash
# 临时
sysctl -w net.ipv4.tcp_congestion_control=bbr
# 永久写进 /etc/sysctl.conf
echo "net.ipv4.tcp_congestion_control=bbr" >> /etc/sysctl.conf
sysctl -p
四、生产环境 TCP 内核参数调优
这一节是上线高并发服务(网关、消息队列、数据库)时最常用的。先讲原则:不要照抄网上"一键高并发"脚本,先搞清每个参数管什么、当前值是多少,再按需调。
4.1 连接队列(防 SYN 攻击 / 防 accept 不过来)
| 参数(Linux) | 作用 | 典型建议 |
|---|---|---|
net.core.somaxconn |
全连接队列(Accept Queue)上限,默认 5.4+ 是 4096 | 高并发调大,如 65535 |
net.ipv4.tcp_max_syn_backlog |
半连接队列(SYN Queue)上限 | 高并发调大,如 8192~65535 |
net.ipv4.tcp_syncookies |
SYN 洪水防护 | 保持 1 开启 |
macOS 没有完全对应的
somaxconn(listen()直接受 backlog 限制),可理解为概念对照。
4.2 TIME_WAIT(上篇讲过,这里汇总调优)
| 参数 | 作用 | 注意 |
|---|---|---|
net.ipv4.tcp_fin_timeout |
TIME_WAIT 时长,默认 60 秒 | 别乱改小,否则旧报文来不及过期 |
net.ipv4.tcp_tw_reuse |
复用 TIME_WAIT | 只对出站连接生效,且必须开 tcp_timestamps=1;Linux 4.12 后行为更严 |
net.ipv4.ip_local_port_range |
出站临时端口范围,默认 32768 60999 |
短连接多可调成 1024 65535 |
⚠️ 老文章里的 tcp_tw_recycle 在 Linux 4.12 已删除,NAT 后还会出问题,别再开。
4.3 缓冲区(跑满带宽的关键)
| 参数 | 作用 |
|---|---|
net.core.rmem_default / rmem_max |
全局 socket 接收缓冲区默认/最大值 |
net.core.wmem_default / wmem_max |
全局 socket 发送缓冲区默认/最大值 |
net.ipv4.tcp_rmem |
TCP 专用接收缓冲,三个值 min default max |
net.ipv4.tcp_wmem |
TCP 专用发送缓冲,三个值 min default max |
tcp_rmem / tcp_wmem 是三个数:最小 默认 最大值,内核在这个范围内自动伸缩。本机 macOS 对应值:
bash
$ sysctl net.inet.tcp.sendspace net.inet.tcp.recvspace
net.inet.tcp.sendspace: 131072 # 128 KB
net.inet.tcp.recvspace: 131072
4.4 拥塞控制与辅助开关
| 参数 | 作用 |
|---|---|
net.ipv4.tcp_congestion_control |
当前拥塞算法(cubic / bbr) |
net.ipv4.tcp_available_congestion_control |
可用算法列表 |
net.ipv4.tcp_sack |
选择性确认,默认 1 开 |
net.ipv4.tcp_timestamps |
时间戳,默认 1(tw_reuse 的前提) |
net.ipv4.tcp_window_scaling |
窗口扩大,默认 1,高速链路必须 |
net.ipv4.tcp_no_metrics_save |
关闭后不缓存旧连接指标,避免老经验拖累新连接 |
4.5 Keepalive(探活,别和"连接保活"混)
| Linux 参数 | 默认 | 作用 |
|---|---|---|
net.ipv4.tcp_keepalive_time |
7200 秒 | 连接空闲多久后开始发探测 |
net.ipv4.tcp_keepalive_intvl |
75 秒 | 探测包间隔 |
net.ipv4.tcp_keepalive_probes |
9 次 | 连续多少次没响应判定断开 |
macOS 对应:net.inet.tcp.keepidle=7200000(毫秒)、keepintvl=75000、keepcnt=8。
⚠️ keepalive 是"死连接探活",不是应用层的心跳。它默认空闲 2 小时才开始探测,所以中间 NAT 设备早把端口映射清了。要保活得在应用层发心跳(或把 keepalive_time 调小)。
4.6 高并发服务器推荐 /etc/sysctl.conf 示例
ini
# 连接队列
net.core.somaxconn = 65535
net.ipv4.tcp_max_syn_backlog = 65535
net.ipv4.tcp_syncookies = 1
# TIME_WAIT / 端口
net.ipv4.tcp_fin_timeout = 30
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_timestamps = 1
net.ipv4.ip_local_port_range = 1024 65535
# 缓冲区(按 BDP 算,见下节)
net.core.rmem_max = 67108864
net.core.wmem_max = 67108864
net.ipv4.tcp_rmem = 4096 131072 67108864
net.ipv4.tcp_wmem = 4096 16384 67108864
# 拥塞控制
net.ipv4.tcp_congestion_control = bbr
net.ipv4.tcp_sack = 1
net.ipv4.tcp_window_scaling = 1
# keepalive 探活(按需调小)
net.ipv4.tcp_keepalive_time = 600
net.ipv4.tcp_keepalive_intvl = 30
net.ipv4.tcp_keepalive_probes = 5
改完生效:sysctl -p。改前先 sysctl <参数名> 记一下原值,出问题能回滚。
五、缓冲区要开多大?用 BDP 算
🔴 这是缓冲区调优的核心公式:带宽延迟积 BDP(Bandwidth-Delay Product)。
BDP = 带宽 × 往返延迟 RTT
含义:管道里"在途的数据"最多就是带宽乘时间。发送/接收缓冲区必须至少等于 BDP,才可能跑满这条链路的带宽------小于 BDP,窗口没撑开就停了,带宽白白浪费。
算个例子:1 Gbps 带宽、RTT 50ms:
BDP = 1 Gbps × 0.05 s
= 1 × 10^9 bit/s × 0.05 s
= 5 × 10^7 bit
= 6.25 × 10^6 Byte
≈ 6.25 MB
所以这条链路的缓冲区建议开到 ≥ 6.25 MB ,这就是为什么跨国、高带宽服务器要把 tcp_rmem/tcp_wmem 的 max 开到几十 MB。本地 loopback RTT 接近 0,128KB 默认就够------别盲目把生产值照搬到本地。
六、实测:看当前拥塞算法与缓冲区
bash
# 看当前拥塞算法(Linux)
$ sysctl net.ipv4.tcp_congestion_control
net.ipv4.tcp_congestion_control = cubic
# 看本机 macOS 缓冲区
$ sysctl net.inet.tcp.sendspace net.inet.tcp.recvspace
net.inet.tcp.sendspace: 131072
net.inet.tcp.recvspace: 131072
有条件的话,可以用 iperf3 在两台机器间跑吞吐,切换拥塞算法前后对比:
bash
# 服务端
iperf3 -s
# 客户端打流,观察吞吐量
iperf3 -c <服务端IP> -t 10
# 切到 bbr 后再跑一次,对比吞吐变化(尤其在高 RTT 链路上)
总结:你真正需要记住的 8 件事
- 流量控制管接收方,拥塞控制管网络 ;实际窗口 =
min(rwnd, cwnd)。 - 经典四件套:慢启动(指数)→ 拥塞避免(线性)→ 快重传 → 快恢复。
- 丢包分两种:超时 = 严重拥塞,cwnd 砍到 1 重来 ;3 个重复 ACK = 轻微拥塞,减半进快恢复。
- Linux 默认是 Cubic 不是 Reno;BBR 不靠丢包、靠测带宽和 RTT,长肥网络更猛。
- 连接队列看
somaxconn+tcp_max_syn_backlog,SYN 攻击靠syncookies。 - TIME_WAIT 靠
tcp_tw_reuse(需开时间戳)+ 调大ip_local_port_range;tcp_tw_recycle已废弃。 - 缓冲区别瞎调,用 BDP = 带宽 × RTT 算,1Gbps/50ms ≈ 6.25MB 起步。
- keepalive 是死连接探活(默认空闲 2 小时),不等于应用层心跳。
验证清单
- 能说清流量控制和拥塞控制的区别
- 能默画 cwnd 锯齿曲线,指出哪段指数、哪段线性、为什么砍半
- 能说清超时和 3 个重复 ACK 两种丢包的不同反应
- 知道 Linux 默认拥塞算法是 Cubic,BBR 的优势场景
- 能用
sysctl net.ipv4.tcp_congestion_control看当前算法 - 能说出 somaxconn、tcp_max_syn_backlog、tcp_tw_reuse 各管什么
- 会用 BDP 公式算一条链路需要多大缓冲区
- 知道 keepalive 默认 2 小时,保活要靠应用层心跳
参考资源
- RFC 9293(TCP 标准,含拥塞控制框架):https://www.rfc-editor.org/rfc/rfc9293
- RFC 5681(TCP 拥塞控制 Reno 详细算法):https://www.rfc-editor.org/rfc/rfc5681
- Linux man 手册
man 7 tcp(全部内核参数权威说明):https://man7.org/linux/man-pages/man7/tcp.7.html - Linux 内核文档:ip-sysctl.txt(tcp_rmem/tcp_wmem 等参数):https://www.kernel.org/doc/Documentation/networking/ip-sysctl.txt
- Google BBR 论文(BBR: Congestion-Based Congestion Control):https://queue.acm.org/detail.cfm?id=3022184
- iperf3 官方文档:https://iperf.fr/iperf-doc.php
TCP 三部曲到此结束。下一篇《网络 12:TCP 抓包实战》讲:用 tcpdump / Wireshark 真实抓一条连接的完整握手挥手、对着报文逐字段拆解 seq/ack/窗口/选项,以及怎么用抓包定位"连接被拒""粘包""重传"这些线上问题。