前言
TCP 是面向连接的可靠传输协议,连接的建立与释放分别通过「三次握手」和「四次挥手」完成。理解这两个过程,是掌握 TCP 状态机、排查网络问题以及应对面试的基础。
一、为什么 TCP 需要握手
TCP 通信前必须建立连接,核心目的是确认双方具备收发能力、同步初始序列号(ISN),并避免历史连接请求对当前连接造成干扰。序列号负责后续数据包的有序传递与可靠确认,因此双方都需要在连接建立阶段交换各自的初始序列号。
二、TCP 三次握手
三次握手的完整流程如下:
1. 第一次握手:客户端发送 SYN
客户端生成初始序列号 x,向服务器发送 SYN=1, seq=x 报文,随后进入 SYN_SENT 状态。
2. 第二次握手:服务器回复 SYN 和 ACK
服务器收到报文后,为该连接分配资源,生成自己的初始序列号 y,并回复 SYN=1, ACK=1, seq=y, ack=x+1,进入 SYN_RCVD 状态。
3. 第三次握手:客户端发送 ACK
客户端收到后,回复 ACK=1, seq=x+1, ack=y+1,自身进入 ESTABLISHED 状态。服务器收到该 ACK 后也进入 ESTABLISHED 状态,连接建立完成。
用时序图表示如下:
为什么是三次握手而不是两次
两次握手无法可靠同步双方的初始序列号,也无法防止旧的连接请求造成干扰。例如,客户端第一次发送的 SYN 在网络中滞留很久后重新到达服务器,若服务器立即建立连接并回包,客户端却早已放弃该请求,就会产生一个无效连接。三次握手通过让客户端最终确认,避免这种历史报文被误用。
从能力验证角度看,三次握手后双方都能确认:自己的发送能力正常、接收能力正常,对方也具备收发能力。
三、TCP 四次挥手
TCP 支持全双工通信,两个方向的数据发送需要分别关闭,因此释放连接通常需要四次交互。
1. 第一次挥手:主动关闭方发送 FIN
主动关闭方发送 FIN=1, seq=u,进入 FIN_WAIT_1 状态。此后它不再发送数据,但仍可接收对方数据。
2. 第二次挥手:被动关闭方回复 ACK
被动方收到 FIN 后,回复 ACK=1, ack=u+1,进入 CLOSE_WAIT 状态。主动方收到 ACK 后进入 FIN_WAIT_2 状态。此时被动方到主动方的方向可能仍有数据在传输。
3. 第三次挥手:被动关闭方发送 FIN
被动方数据发送完毕后,发送 FIN=1, seq=v,进入 LAST_ACK 状态。
4. 第四次挥手:主动关闭方回复 ACK
主动方收到 FIN 后,回复 ACK=1, ack=v+1,进入 TIME_WAIT 状态,等待 2MSL 时间后关闭。被动方收到 ACK 后立即关闭连接。
用时序图表示如下:
为什么是四次挥手而不是三次
被动方收到 FIN 时,可能还有数据没有发送完毕,因此它需要先单独回复 ACK 表示「我收到了关闭请求」,等数据全部发送完成后再发送 FIN。这两个动作无法合并,所以挥手通常为四次。如果被动方恰好没有数据要发送,第二次的 ACK 和第三次的 FIN 可以合并,此时会退化为三次挥手。
TIME_WAIT 状态的作用
主动关闭方在发送最后一个 ACK 后会进入 TIME_WAIT 状态,并等待 2MSL 的时间,原因主要有两点:
- 确保最后的 ACK 能被对方收到:如果该 ACK 丢失,被动方会重传 FIN,主动方仍有机会再次回复 ACK。
- 让旧连接的数据包在网络中自然消失:避免同源同目的端口的旧数据包进入新连接,造成数据混乱。
四、常见问题梳理
| 问题 | 简要解释 |
|---|---|
| 第三次握手失败会怎样 | 服务器在 SYN_RCVD 状态重发 SYN+ACK,超过次数后释放半连接资源。 |
| SYN 洪泛攻击如何防范 | 常见手段包括限制半连接数量、缩短 SYN 超时时间、启用 SYN Cookie 等。 |
| 为什么 TIME_WAIT 是 2MSL | 保证最后一个 ACK 可达,并让旧连接报文在网络中消散。 |
| 挥手一定是四次吗 | 不一定,若被动方无数据发送,可将第二次和第三次合并,变为三次挥手。 |
五、总结
三次握手解决的是「连接能不能建立、初始序列号如何同步」的问题,四次挥手解决的是「两个方向的数据如何安全关闭」的问题。抓住状态变化这条主线,再结合实际抓包观察 SYN、FIN、ACK 报文,就能更直观地理解整个 TCP 连接的生命周期。