TCP 三次握手与四次挥手详解
TCP 是面向连接的、可靠的传输层协议。建立连接时的「三次握手」和断开连接时的「四次挥手」是计算机网络中最经典的知识点,也是 408 和面试的高频考点。
三次握手(Three-Way Handshake)
TCP 建立连接需要三个步骤,确保双方都能发送和接收数据。
mermaid
sequenceDiagram
participant C as 客户端
participant S as 服务端
C->>S: SYN, seq=x
Note right of S: SYN_RCVD
S->>C: SYN+ACK, seq=y, ack=x+1
Note left of C: ESTABLISHED
C->>S: ACK, ack=y+1
Note right of S: ESTABLISHED每一步发生了什么
- 第一次握手(SYN):客户端发送 SYN 报文(SYN=1, seq=x),进入 SYN_SENT 状态。
- 第二次握手(SYN+ACK):服务端回复 SYN+ACK(SYN=1, ACK=1, seq=y, ack=x+1),进入 SYN_RCVD 状态。
- 第三次握手(ACK):客户端发送 ACK(ACK=1, ack=y+1),双方进入 ESTABLISHED 状态。连接建立完成。
为什么是三次而不是两次?
核心原因:防止已失效的连接请求到达服务端,导致资源浪费。
假设只有两次握手:客户端发送了一个 SYN,但因为网络延迟迟迟没到达。客户端超时后重发 SYN,完成了连接和数据传输。此后那个迟到的 SYN 到达服务端,服务端以为是新连接,回复 SYN+ACK 后进入 ESTABLISHED,一直等待数据——但客户端根本不会发送,服务端白白浪费资源。
三次握手可以避免这个问题:服务端回复 SYN+ACK 后处于 SYN_RCVD,如果没有收到第三次 ACK,不会真正建立连接。
四次挥手(Four-Way Wavehand)
TCP 是全双工的,断开连接需要双方各自独立关闭自己的发送通道。
mermaid
sequenceDiagram
participant C as 主动关闭方
participant S as 被动关闭方
C->>S: FIN, seq=u
Note left of C: FIN_WAIT_1
S->>C: ACK, ack=u+1
Note right of S: CLOSE_WAIT
Note left of C: FIN_WAIT_2
S->>C: FIN, seq=w
Note right of S: LAST_ACK
C->>S: ACK, ack=w+1
Note left of C: TIME_WAIT 2MSL
Note right of S: CLOSED
Note left of C: CLOSED- 第一次挥手(FIN):主动关闭方发送 FIN,表示「我没有数据要发了」。
- 第二次挥手(ACK):被动关闭方回复 ACK。此时被动关闭方可能还有数据要发,处于 CLOSE_WAIT 半关闭状态。
- 第三次挥手(FIN):被动关闭方发送完剩余数据后,发送 FIN。
- 第四次挥手(ACK):主动关闭方回复 ACK,进入 TIME_WAIT 状态,等待 2MSL 后关闭。
为什么主动关闭方要 TIME_WAIT?
TIME_WAIT 持续 2MSL(Maximum Segment Lifetime),有两个原因:
- 保证最后一个 ACK 到达:如果 ACK 丢失,被动关闭方会重发 FIN。主动关闭方在 TIME_WAIT 期间还能收到重传的 FIN 并重新发送 ACK。
- 防止旧报文干扰新连接:2MSL 足够让本次连接中的残留报文在网络中消失。
MSL 是报文最大生存时间,RFC 793 建议 2 分钟,Linux 默认 30 秒。
常见面试题速记
| 问题 | 关键回答 |
|---|---|
| 为什么三次握手? | 防止失效的连接请求浪费服务端资源 |
| 为什么四次挥手? | TCP 全双工,每个方向需单独关闭 |
| TIME_WAIT 为什么 2MSL? | 保证最后 ACK 到达 + 让旧报文消失 |
| SYN 攻击是什么? | 伪造大量 SYN,服务端 SYN_RCVD 队列满 |
小结
三次握手确保连接双向可达,四次挥手确保全双工通道各自独立关闭。TIME_WAIT 是设计上的必要妥协。理解每个状态机的转换条件,是掌握 TCP 的基础。