🤔 为什么必须是 TCP 三次握手?

从失败案例看三次握手的必要性

客户端发送
服务器发送
延迟的旧报文
场景:如果是两次握手
假设只握手一次会怎样?
💀 问题:历史连接请求导致服务器资源浪费
💻
客户端
状态
🖥️
服务器
状态
T1
🟢 SYN (旧的延迟报文)
Seq: 100
这是一次之前发起的连接请求
因网络延迟,很晚才到达服务器
T2
🟢 SYN (新的请求)
Seq: 200
客户端发起新的连接请求
T3
🟡 SYN + ACK
服务器以为是新请求
直接建立连接,分配资源!

⚠️ 造成的问题

旧的延迟 SYN 被服务器当作新连接,建立了一个永远不会使用的虚假连接,浪费了服务器的资源(内存、端口、进程等)。

❌ 两次握手:无法识别历史连接,造成资源浪费
场景:序列号无法同步
两次握手能同步双方的序列号吗?
💀 问题:服务器不知道客户端的初始序列号
💻
客户端
🖥️
服务器
🟢 SYN
Seq: x
(客户端的初始序列号)
🟡 SYN + ACK
Seq: y, ACK: x+1
(服务器自己的序列号 + 确认客户端)

⚠️ 问题:服务器不知道客户端是否收到了 y

服务器不知道客户端是否成功收到了自己的初始序列号 y。如果网络丢包,客户端不知道服务器从 y 开始计数,导致后续数据传输错乱!

数据流示意:
客户端认为:
我的数据
等待确认
服务器认为:
服务器数据
等待确认
实际应该:
x+1
y+1
❌ 两次握手:无法确认双方都知道了对方的初始序列号
三次握手:完美解决所有问题
为什么三次就能行?
✨ 第三次握手让客户端确认服务器愿意通信
💻
客户端
ISN = x
🖥️
服务器
ISN = y
🟢 第一次:SYN
Seq: x
我想连接你
🟡 第二次:SYN + ACK
Seq: y, ACK: x+1
我同意,并告诉你我从 y 开始
🔴 第三次:ACK
Seq: x+1, ACK: y+1
我收到你的 y 了,双方都确认,可以开始通信!
序列号同步完成:
客户端知道:
x ~ x+1
y ~ y+1
服务器知道:
x ~ x+1
y ~ y+1
✓ 双方都知道了对方的初始序列号,数据传输不会错乱
✅ 三次握手:双方确认、同步序列号、避免资源浪费
💡
三次握手的三个核心目的
每一次握手都在验证什么?
🔒

第一次握手

验证:服务器的接收能力
客户端说:"我想和你通信"(发送能力验证)
服务器知道:客户端能发送,我能接收

🔓

第二次握手

验证:客户端的接收能力 + 服务器的发送能力
服务器说:"我同意,而且你能收到我"(双向验证)
客户端知道:我能接收,服务器能发送

第三次握手

验证:客户端确认服务器愿意通信
客户端说:"我确认收到,咱俩开始吧"
双方确认:都知道对方准备好了

为什么不四次握手?
效率与可靠性的平衡

❌ 四次握手(浪费)

1. 客户端 → 服务器:SYN
2. 服务器 → 客户端:ACK
3. 服务器 → 客户端:SYN
4. 客户端 → 服务器:ACK

问题:ACK 和 SYN 可以合并,为什么要分开?
VS

✅ 三次握手(最优)

1. 客户端 → 服务器:SYN
2. 服务器 → 客户端:SYN + ACK
3. 客户端 → 服务器:ACK

优化:服务器的 SYN 和 ACK 合并为一次

TCP 设计原则:用最少的次数完成可靠的连接建立

三次是最优解:既能保证可靠性,又不浪费资源

📋 总结:三次握手缺一不可