TCP采用三次握手(Three-way Handshake)而不是二次握手,主要是为了解决网络中的延迟重复报文 和初始序列号同步问题,确保连接的可靠性和安全性。以下是具体原因:
1. 防止历史重复连接导致的资源浪费
- 问题场景:如果客户端发送的第一次握手报文(SYN)因网络延迟滞留,客户端会重发一个新的SYN。若采用二次握手,服务端在收到滞留的旧SYN后会直接建立连接(发送SYN-ACK后认为连接已建立),但客户端会忽略这个响应(因为序列号不匹配),导致服务端长期等待,浪费资源。
- 三次握手的解决:服务端在收到SYN后会等待客户端的第三次ACK确认(第三次握手),只有收到ACK后才真正建立连接。若ACK未到达,服务端会超时关闭连接,避免资源浪费。
场景1:旧SYN 早于 新SYN到达(常规情况)
-
客户端发送旧SYN(延迟滞留)
- 客户端发送
SYN=1, seq=100
(旧SYN),但该报文在网络中滞留,未到达服务端。 - 客户端未收到
SYN-ACK
,触发超时重传。
- 客户端发送
-
客户端发送新SYN(正常到达)
- 客户端重传
SYN=1, seq=300
(新SYN),新SYN先到达服务端。 - 服务端回复
SYN-ACK, seq=500, ack=301
(确认客户端的seq=300
)。 - 此时连接尚未建立 !服务端处于
SYN_RCVD
状态,等待客户端的第三次ACK
。
- 客户端重传
-
客户端回复第三次ACK
- 客户端收到
SYN-ACK
后,发送ACK=1, seq=301, ack=501
(确认服务端的seq=500
)。 - 至此,三次握手完成 ,连接正式建立(服务端进入
ESTABLISHED
状态)。
- 客户端收到
-
滞后的旧SYN到达服务端(关键区别)
- 旧SYN
SYN=1, seq=100
终于到达服务端。 - 服务端误认为这是一个新连接,回复
SYN-ACK, seq=600, ack=101
。 - 但客户端会忽略此
SYN-ACK
(因为当前连接已建立,且ack=101
不匹配客户端的序列号seq=301
)。 - 服务端因收不到第三次
ACK
(ack=601
),会超时关闭这个半连接(避免资源浪费)。
- 旧SYN
如果是 二次握手 会怎样?
假设TCP采用二次握手(即服务端发送 SYN-ACK
后直接认为连接建立):
- 当旧SYN
SYN=1, seq=100
到达服务端时:- 服务端回复
SYN-ACK, seq=600, ack=101
,并立即认为连接已建立。 - 客户端因序列号不匹配(期望
ack=301
),忽略此SYN-ACK
。 - 结果:服务端为旧SYN维护了一个无效连接,直到超时。
- 服务端回复
关键区别
步骤 | 三次握手 | 二次握手 |
---|---|---|
服务端收到SYN后的行为 | 发送 SYN-ACK ,等待第三次 ACK 才建立连接。 |
发送 SYN-ACK 后立即建立连接。 |
对延迟旧SYN的处理 | 因无第三次 ACK ,服务端会超时关闭无效连接。 |
服务端会维持无效连接,直到超时(资源浪费)。 |
安全性 | 防止历史SYN干扰,确保连接唯一性。 | 可能因延迟SYN建立多余连接,易受攻击(如SYN洪泛)。 |
场景2:旧SYN 晚于 新SYN到达
- 客户端发送
SYN=1, seq=100
(旧SYN),延迟滞留。 - 客户端重传
SYN=1, seq=300
(新SYN),新SYN先到达服务端 :- 服务端回复
SYN-ACK, seq=500, ack=301
,连接正常建立(三次握手完成)。
- 服务端回复
- 延迟的旧SYN到达服务端 :
- 服务端误认为是新连接,回复
SYN-ACK, seq=600, ack=101
。 - 客户端发现
ack=101
不匹配(当前序列号已是301
),忽略该SYN-ACK。 - 如果是二次握手:服务端会为旧SYN建立另一个无效连接。
- 三次握手解决 :服务端因收不到ACK(
ack=601
),不会建立连接。
- 服务端误认为是新连接,回复
关键结论
- 无论旧SYN早到还是晚到 ,二次握手都会导致服务端可能建立无效连接。
- 旧SYN早到:客户端不认可,服务端空等。
- 旧SYN晚到:服务端多建一个无用连接。
- 三次握手的核心作用 :
- 通过第三次ACK确认双方序列号,过滤所有延迟或重复的SYN。
- 服务端仅在收到ACK后才会真正分配资源,避免资源浪费。
现实类比
假设你两次通知同事开会:
- 第一次通知(旧):"周一开会"(延迟未送达)。
- 第二次通知(新):"周二开会"(同事收到并回复"收到")。
- 如果旧通知后来到达 :
- 二次握手:同事看到"周一开会"直接准备会议室(浪费资源)。
- 三次握手:同事会等你确认"周一开会"的回复(但你不会回复,因为已改时间),同事放弃准备。
- 如果旧通知后来到达 :
为什么不能依赖"新SYN一定先到"?
网络延迟是不可控的,可能因路由变化、拥塞、重传等导致报文乱序。TCP的设计必须假设最坏情况(即旧报文可能在任何时候到达),而三次握手是解决这一问题的最小成本方案。
三次握手 vs 二次握手 的核心差异
对比项 | 三次握手 | 二次握手 |
---|---|---|
服务端何时认为连接建立? | 收到客户端的 第三次 ACK 后才建立 | 发送 SYN-ACK 后 立即认为连接已建立 |
无效连接的处理 | 服务端在 SYN_RCVD 状态等待 ACK ,未建立完整连接,超时关闭(资源占用较少) |
服务端 已认为连接建立 (ESTABLISHED ),但客户端不认可,导致 完全无效的连接 占用资源 |
资源占用情况 | 仅维护 半连接队列(SYN队列),占用较少 | 维护 全连接队列,占用更多资源(如 socket、内存等) |
受攻击风险(如SYN洪泛) | 更容易防御(未完成握手的连接可快速清理) | 更危险(攻击者伪造SYN可快速耗尽服务端连接资源) |
为什么说"三次握手更优"?
虽然最终都会超时关闭,但 三次握手 在以下方面更优:
-
资源占用更少
- 三次握手下,服务端在收到
SYN
后仅进入SYN_RCVD
状态(半连接),占用资源较少。 - 二次握手下,服务端直接进入
ESTABLISHED
状态,分配完整连接资源(如 socket、缓冲区等),即使客户端不认可,这些资源也会被占用直到超时。
- 三次握手下,服务端在收到
-
防止SYN洪泛攻击
- 攻击者可以伪造大量
SYN
报文,消耗服务端资源。 - 三次握手 :服务端仅维护半连接队列,超时时间较短(如Linux默认
SYN_TIMEOUT=60s
),能更快清理无效请求。 - 二次握手 :服务端会为每个
SYN
分配完整连接资源,更容易被攻击打满。
- 攻击者可以伪造大量
-
避免"滞留旧SYN"建立无效连接
- 即使旧
SYN
晚到,三次握手下服务端仍会因收不到ACK
而关闭,不会建立实际可用的无效连接。 - 二次握手下,服务端会为旧
SYN
分配资源,尽管客户端不认可,但仍可能影响服务端性能(如占用端口、内存等)。
- 即使旧
举例说明
假设服务端最大连接数为 1000:
- 三次握手 :攻击者发送 1000 个
SYN
,服务端仅维护 1000 个半连接(SYN_RCVD
),不会影响已建立的正常连接。 - 二次握手 :攻击者发送 1000 个
SYN
,服务端直接建立 1000 个无效的ESTABLISHED
连接,导致正常用户无法访问。
结论
虽然最终都会超时关闭,但 三次握手 在 资源占用、安全性、抗攻击能力 上明显优于二次握手。
核心区别 :三次握手下,服务端在收到 ACK
前 不会分配完整连接资源,而二次握手会过早分配,导致更高的风险。
总结
- 三次握手的必要性 :通过第三次
ACK
确认双方的初始序列号,并过滤延迟或重复的SYN
。 - 二次握手的问题:服务端无法区分有效连接和延迟的旧SYN,会导致资源浪费。
- 你的质疑是正确的 :在之前的描述中,"连接建立"的说法不严谨,必须明确第三次
ACK
的作用。
2. 同步双方的初始序列号(ISN)
- TCP需要通过序列号保证数据有序性和可靠性。三次握手确保双方双向序列号的同步 :
- 客户端发送SYN(携带自己的ISN)。
- 服务端回复SYN-ACK(携带自己的ISN并确认客户端的ISN)。
- 客户端发送ACK(确认服务端的ISN)。
- 二次握手的缺陷:若只有两次握手,服务端无法确认客户端是否收到了自己的ISN(即客户端可能未准备好接收数据)。
3. 避免恶意攻击(如SYN洪泛)
- 二次握手会让服务端在发送SYN-ACK后立即建立连接,攻击者可以伪造大量SYN报文耗尽服务端资源(SYN Flood攻击)。
- 三次握手的缓解:服务端在收到第三次ACK前仅维护半连接状态(如SYN队列),未完成的连接会被超时清除,降低资源占用。
4. 双向信道可靠性确认
- TCP是全双工协议,需要确保双向通信 的可靠性。三次握手明确双方均具备收发能力:
- 客户端→服务端:SYN证明客户端发送正常。
- 服务端→客户端:SYN-ACK证明服务端收发正常。
- 客户端→服务端:ACK证明客户端接收正常。
二次握手的问题总结
如果仅用两次握手:
- 服务端无法区分滞留的旧SYN报文 和正常新连接。
- 服务端无法确认客户端是否收到了自己的SYN-ACK,可能导致单向连接(客户端未准备好接收数据)。
- 更易受到资源耗尽攻击。
类比现实场景
想象两人通话:
- A:"你能听到我吗?"(SYN)
- B:"我能听到你,你能听到我吗?"(SYN-ACK)
- A:"我也能听到你!"(ACK)
此时双方才确认通信正常。若只有前两步,A无法确认B是否听到了自己的第二次回应。
总结:三次握手是平衡可靠性和效率的最小次数,既避免了历史报文干扰,又确保了双向通信的初始序列号同步,同时增强了抗攻击能力。