02 · TCP 握手 / 挥手与拥塞控制
面试爱考、排障也常用:三次握手为什么要三次、四次挥手为什么有 TIME_WAIT、丢包重传与拥塞控制怎么配合。本篇用"打电话/寄快递"的直觉讲透,并给前后端真正用得上的判断。
速览卡
| 项 | 内容 |
|---|---|
| 一句话定位 | TCP 连接建立、断开与可靠传输的直觉理解 |
| 核心内容 | 三次握手、四次挥手、TIME_WAIT 与 CLOSE_WAIT、重传与拥塞控制 |
| 读完能回答 | 为什么握手是三次不是两次?服务端大量 CLOSE_WAIT 说明什么? |
| 相关板块 | N01 网络分层 | N12 IO 模型 | N11 抓包 |
一、三次握手:确认"双方都能收发"
text
客户端 ── SYN ────────────────> 服务端 "我想连你"
客户端 <── SYN + ACK ────────── 服务端 "收到,我也能回"
客户端 ── ACK ────────────────> 服务端 "我也能收"为什么是三次:两次不够——服务端无法确认"自己发的能被客户端收到"。 为什么不是四次:SYN 和 ACK 可以合并成一次,省一个 RTT。
一次握手 = 一个 RTT(往返时延)。HTTP 请求前多一次 TCP 握手 = 多一个 RTT 的延迟。这就是 Keep-Alive/HTTP2 复用连接的意义。
二、四次挥手:谁都有话说完了才能关
text
主动方 ── FIN ────────────────> 被动方 "我要关了"
主动方 <── ACK ──────────────── 被动方 "收到,但我还有话"
主动方 <── FIN ──────────────── 被动方 "我也说完了"
主动方 ── ACK ────────────────> 被动方 "好,关"四次是因为关闭是双向独立的:每一方都要单独"说完再关"。
三、TIME_WAIT 与 CLOSE_WAIT:排障高频词
| 状态 | 谁 | 含义 | 排障意义 |
|---|---|---|---|
| TIME_WAIT | 主动关闭方 | 等 2MSL 再彻底释放,防旧包串扰 | 量大正常,但异常堆积要查连接复用 |
| CLOSE_WAIT | 被动关闭方 | 收到 FIN 但应用没关 socket | 说明应用层 bug:没调用 close |
| ESTABLISHED | 双方 | 正常连接 | — |
判断框架:服务端出现大量
CLOSE_WAIT→ 是代码没释放连接,不是网络问题。TIME_WAIT多 → 是主动关闭方建连/断连太频繁,优先做连接复用而不是调内核参数。
四、可靠传输:确认 + 重传
text
发数据 ──> 等 ACK
没收到 ACK → 重传(指数退避)| 机制 | 解决什么 |
|---|---|
| 序列号 | 乱序重组、去重 |
| 确认 ACK | 知道对方收到了 |
| 超时重传 | 没确认就重发 |
| 快速重传 | 收到重复 ACK 提前重发,不等超时 |
对应用的影响:网络差 → RTT 上升 → 重传 → 更慢。观察"丢包率"比"延迟"更能解释诡异变慢。
五、拥塞控制:别把网络堵死
TCP 不只是"双方协商",还要照顾整个网络的负载:
text
慢启动 刚开始发送慢,指数增长试探
拥塞避免 到阈值后线性增长
快恢复 丢包后降速但不归零核心思想:丢包被当成"网络堵了"的信号,主动降速,避免崩溃式拥塞。 这就是为什么"越重传越慢"——不是 TCP 傻,是它在保护网络。
六、自检清单
- [ ] 能画出三次握手,说出为什么是三次
- [ ] 知道一次握手 = 一个 RTT,理解连接复用的价值
- [ ] 能画出四次挥手,说出为什么是四次
- [ ] 看到大量 CLOSE_WAIT 能想到"应用没释放连接"
- [ ] 看到 TIME_WAIT 多能想到"建连太频繁"
- [ ] 知道丢包触发重传与降速,理解"越重传越慢"
七、常见误区
| 误区 | 事实 |
|---|---|
| "四次挥手可以省成三次" | 双向独立关闭,ACK 与 FIN 不能总合并 |
| "CLOSE_WAIT 是网络问题" | 通常是应用没 close socket |
| "TIME_WAIT 要赶紧调没" | 它是防旧包串扰的安全机制,先做连接复用 |
| "重传是 bug" | 是可靠传输的正常机制,丢包多才是问题 |
| "握手三次浪费" | 三次已是最小安全次数 |
八、动手练习
练习 A:看连接状态
netstat -an | findstr 8080(Windows)或 ss -s(Linux),观察本机/服务端的 TIME_WAIT 与 CLOSE_WAIT 数量,并解释你看到的值。
练习 B:复现 CLOSE_WAIT
写一个简易服务端,收到请求后不 close 连接,用浏览器连几次,观察连接状态变化。
练习 C:算 RTT 影响
HTTP/1.1 每次请求约 2~3 个 RTT(TCP 握手 + 请求响应)。算一下从北京访问 300ms RTT 的海外服务的首屏延迟构成,再算 HTTP2 连接复用时能省多少。
相关文档
本区
- 01 · 网络分层 —— TCP 在传输层的位置
- 11 · 抓包与网络调试 —— 用抓包看握手/挥手
- 12 · IO 模型 —— 服务端连接管理
其他域
- 04 · 浏览器与网络 —— 浏览器侧的连接复用
- 07 · 服务端性能与容量 —— 连接池与高并发