TCP 为什么要三次握手?两次不行吗?
简化版
三次握手是为了让双方都确认「我能发、你能收;你能发、我能收」这两条链路都通,同时同步各自的初始序列号(ISN)。两次不行,因为两次握手结束时,服务端无法确认客户端到底有没有收到自己发的 SYN+ACK,也无法防止历史失效的连接请求造成错误连接。
详细版
三次握手的过程:
- 第一次:客户端发
SYN,带上自己的初始序列号seq=x——「我想建连,我的序号从 x 开始」。 - 第二次:服务端回
SYN + ACK,ack=x+1(确认收到客户端的 x),同时带自己的序号seq=y——「收到你的了,我的序号从 y 开始」。 - 第三次:客户端回
ACK,ack=y+1——「也收到你的了」。
为什么必须三次,可以从「双方各自要确认什么」推:
- 第 1、2 次之后,客户端确认了:我发的对方收到了(收到 SYN+ACK),双向都通。
- 但此时服务端只知道「我发出了 SYN+ACK」,不知道客户端有没有收到。
- 第 3 次 ACK,就是客户端告诉服务端「你的我收到了」——至此服务端才确认双向都通。
所以三次是让双方都完成确认的最小次数:SYN 和 ACK 各需要一来一回,共 4 个动作,中间服务端的 ACK 和 SYN 可以合并成一个包,就成了三次。
完整版教学
一、握手的本质:确认「双向都能通」
TCP 是全双工、可靠传输,建连的目标是确保两个方向的通道都畅通。「通道畅通」需要确认四件事:
- 客户端能发(→ 服务端收到 SYN,确认①)
- 服务端能收(同上)
- 服务端能发(→ 客户端收到 SYN+ACK,确认③)
- 客户端能收(同上)
第二次握手一次性确认了 ①②③④ 中客户端视角的部分——客户端确认「双向都通」。但服务端视角里,「客户端能不能收到我的包(③④)」还没被验证。第三次握手补上这个确认。三次,恰好让双方都验证完双向链路。
二、两次为什么不行——两个致命问题
问题一:服务端无法确认自己的包被收到。 如果只握两次,服务端发完 SYN+ACK 就认为连接建立、开始发数据。但万一这个 SYN+ACK 在网络里丢了,客户端根本不知道连接建立了,服务端却在傻傻发数据——单方面建连,浪费资源。
问题二:历史失效连接请求造成错误连接(更经典)。 设想客户端发的一个旧的、在网络里滞留了很久的 SYN 突然到达服务端:
- 两次握手:服务端收到就直接建连、分配资源,但客户端其实早不想连了(那是个过期请求)→ 服务端凭空建了个没人用的连接,白占资源。
- 三次握手:服务端回 SYN+ACK 后,客户端发现「我没发起这个连接」,不会回第三次 ACK(或回 RST),连接建立不起来 → 避免了错误连接。
记忆点:第三次 ACK 的核心价值,就是给客户端一次「否决历史失效请求」的机会。这是很多人只记得「确认收发能力」而漏掉的关键点。
三、为什么要同步初始序列号(ISN)
TCP 靠序列号实现可靠传输(排序、去重、确认、重传)。握手时双方交换各自的 ISN,之后的数据都基于它编号。
ISN 还不能固定(比如都从 0 开始),而是随机生成的。原因:如果 ISN 可预测,一是旧连接的残留数据包可能被误认为新连接的合法数据(序号撞上),二是攻击者能伪造序列号劫持连接。随机 ISN 同时解决了「历史数据混淆」和「安全」两个问题。
四、延伸:SYN Flood 攻击
三次握手有个可被利用的弱点:服务端收到第一次 SYN 后,会分配资源、把连接放进半连接队列,等待第三次 ACK。攻击者用伪造源地址狂发 SYN 但从不回 ACK,就能把半连接队列塞满,正常用户连不进来——这就是 SYN Flood。
常见缓解:
- SYN Cookie:服务端先不分配资源,把连接信息编码进序列号发回去,等第三次 ACK 回来再校验重建,队列不会被占满;
- 调大半连接队列、缩短等待超时;
- 边界设备限速、过滤异常源。
五、常见误区
- ❌ 只答「确认双方收发能力」——漏了「防止历史失效连接」这个关键原因。
- ❌ 以为握手是为了传数据——握手只建连、同步 ISN,不携带应用数据。
- ❌ 以为四次也行、越多越可靠——三次是让双方都确认的最小次数,多余的浪费。(四次挥手是断连,别和建连混。)
- ❌ 以为 ISN 从 0 开始——是随机的,防混淆和劫持。
六、常见误区与追问
| 考点 | 正确口径 |
|---|---|
| 第一次 | 客户端发 SYN,证明客户端发送能力 |
| 第二次 | 服务端回 SYN+ACK,证明服务端收发能力 |
| 第三次 | 客户端回 ACK,证明客户端接收能力并确认服务端 SYN |
Client -> SYN(x)
Server -> SYN(y), ACK(x+1)
Client -> ACK(y+1)
connection established
三次握手要确认的是双向收发能力和初始序列号,不只是“打个招呼”。
- 误区:两次握手也能确认双方可通信。 两次后服务端不知道客户端是否收到了自己的 SYN,无法确认客户端接收能力。
- 误区:三次握手只是为了浪费一次 RTT。 第三次 ACK 能避免历史 SYN 导致服务端误建连接,并确认服务端序列号。
- 误区:ISN 可以固定从 0 开始。 ISN 应随时间变化并具备随机性,降低旧报文混淆和安全风险。
- 追问:为什么第二次 SYN 和 ACK 能合并? 服务端既确认客户端 SYN,又要发送自己的 SYN,同一个报文可同时带两个标志。
- 追问:SYN Flood 利用了什么? 攻击者发送大量 SYN 不完成第三次握手,让服务端半连接状态堆积。
- 追问:握手完成后双方状态是什么? 客户端第三次 ACK 发出后进入 ESTABLISHED,服务端收到 ACK 后进入 ESTABLISHED。
七、加强记忆
三次握手让双方都确认「双向链路畅通」并同步随机初始序列号。两次不行,一是服务端无法确认自己的 SYN+ACK 被收到,二是无法用第三次 ACK 否决「历史失效的连接请求」而导致错误建连。它的副作用是半连接队列可被 SYN Flood 攻击,用 SYN Cookie 等缓解。