前言
TCP(传输控制协议)是一种全双工通信的网络传输协议。
这意味着在 TCP 连接中,通信双方可以同时进行数据的发送和接收,无需等待对方的响应才能继续传输。
彻底理解 TCP 协议需要搞清楚 状态机的变化和时机。
其中一些英语简写的示意如下:
- SYN(Synchronize):可以理解为报文,用于连接建立。
- SYN_SENT(Synchronize Sent):报文已发送。
- SYN_RCVD(Synchronize Received):报文已接收。
- FIN(Finish):请求关闭连接。
- FIN_WAIT_1(Finish Wait 1):客户端关闭请求已发起,等待回执。
- FIN_WAIT_2(Finish Wait 2):回执已收到,等待服务端发起关闭连接请求。此时客户端仍可接收数据,但不能发送。
- TIME_WAIT(Time Wait):服务端关闭请求已收到,发送回执,等待超时断开连接。
- ACK(Acknowledgment):可以理解为确认,表示已成功接收数据,用于发送回执。
- LAST_ACK(Last Acknowledgment):最后一次确认等待状态。
- ESTABLISHED:连接已建立。
- CLOSED:连接已关闭。
一、TCP三次握手
TCP连接过程:
- 客户端 发
SYN(x) - 服务端 收+发
SYN+ACK(x+1, y) - 客户端 收+发
ACK(y+1) - 服务端 收
客户端状态变更:
CLOSED = 发 => SYN_SENT = 收+发 => ESTABLISHED (我发,对方 收+发,我 收+发)
服务端状态变更:
LISTEN = 收+发 => SYN_RCVD = 收 => ESTABLISHED (我 收+发,对方 收+发,我收)
双方都要验证:
- 我发的对方能收到,对方发的我能收到(我可以发送、接收,对方可以接收、发送)
- 并且保持序列号同步,防止因网络延迟导致的序列号混乱(如旧连接的幽灵包)。
对于客户端来说:
- CLOSED = 发=> SYN_SENT:我可以发送
- SYN_SENT = 收+发 => ESTABLISHED:对方可以接收、发送,我可以接收
对于服务端来说:
- LISTEN = 收+发 => SYN_RCVD :对方可以发送,我可以接收、发送
- SYN_RCVD = 收 => ESTABLISHED:对方可以接收
三次握手是数学上的最小可靠解:
- 通过三次消息交换,实现四次能力验证(双方各验证两次)。
- 通过序列号绑定,确保双方对初始状态达成一致。
- 通过状态机转换,确保任何异常都能被检测并恢复(如超时回退到 CLOSED/LISTEN)。
二、TCP四次挥手
TCP断开连接过程:
- 客户端 发
FIN(x) - 服务端 收+发
ACK(x+1) - 客户端 收
- 服务端 发
FIN(y) - 客户端 收+发
ACK(y+1) - 服务端 收
客户端状态变更:
ESTABLISHED = 发 => FIN_WAIT_1 = 收 => FIN_WAIT_2 = 收+发 => TIME_WAIT = 超时 => CLOSED
服务端状态变更:
ESTABLISHED = 收+发 => CLOSE_WAIT = 发 => LAST_ACK = 收 => CLOSED
双方都要验证:
- 我发的终止信号对方能收到
- 对方发的终止信号我能收到
- 确保所有数据都已完整传输(避免数据丢失)
对于客户端来说:
-
ESTABLISHED → FIN_WAIT_1- 动作:发
FIN(x) - 验证点:我可以主动请求关闭发送方向
→ 若发送失败,则保持
ESTABLISHED继续通信。
- 动作:发
-
FIN_WAIT_1 → FIN_WAIT_2- 动作:收
ACK(x+1) - 验证点:对方已接收关闭请求,并回执我ACK(x+1) → 此时客户端仍可接收数据,进入“半关闭”状态。
- 动作:收
-
FIN_WAIT_2 → TIME_WAIT- 动作:收
FIN(y)+ 发ACK(y+1) - 验证点:
- 对方也请求关闭连接(通过FIN包验证)。
- 我可以确认对方的关闭请求(通过ACK包验证)。
- 动作:收
-
TIME_WAIT → CLOSED- 动作:等待2倍的MSL时间(MSL,报文最大生存时间,通常约 2 分钟)
- 验证点:确保所有网络延迟包都已消失
→ 防止旧连接的延迟包干扰新连接。
对于服务端来说:
-
ESTABLISHED → CLOSE_WAIT- 动作:收
FIN(x)+ 发ACK(x+1) - 验证点:
- 对方已请求关闭发送方向(通过FIN包验证)。
- 我可以确认对方的关闭请求(通过ACK包验证)。
→ 此时服务端需检查是否还有数据要发送,若有则继续发送。
- 动作:收
-
CLOSE_WAIT → LAST_ACK- 动作:发
FIN(y) - 验证点:我可以主动请求关闭接收方向
→ 表明服务端已无数据要发送,准备关闭连接。
- 动作:发
-
LAST_ACK → CLOSED- 动作:收
ACK(y+1) - 验证点:对方已确认我的关闭请求
→ 此时服务端完全关闭连接,释放所有资源。
- 动作:收
四次挥手的数学必然性:
-
最小消息交换次数
双方各需发送FIN和接收ACK,共四次消息,无法简化。 -
状态机的完整性
- 客户端通过
TIME_WAIT状态防止最后一个ACK丢失导致服务器无法关闭。 - 服务器通过
CLOSE_WAIT状态确保所有数据发送完毕后再关闭。
- 客户端通过
-
防止幽灵包干扰
TIME_WAIT状态保持2MSL时间,确保:- 本次连接的所有包都已从网络中消失。
- 相同IP和端口的新连接不会收到旧连接的延迟包。
三、四次挥手 vs 三次握手
| 阶段 | 消息交换 | 核心目的 | 状态机保障 |
|---|---|---|---|
| 三次握手 | 3次 | 同步初始序列号,验证双向通信能力 | 防止旧连接幽灵包,资源预分配 |
| 四次挥手 | 4次 | 可靠关闭双向连接,确保数据完整性 | 防止FIN/ACK丢失,幽灵包清理 |
四、关键机制补充
-
半关闭状态(Half-Closed)
- 当客户端处于
FIN_WAIT_2,服务端处于CLOSE_WAIT时,连接处于半关闭状态:- 客户端停止发送,但仍可接收。
- 服务端仍可发送,但需主动发起FIN关闭接收方向。
- 当客户端处于
-
TIME_WAIT的意义
- 确保最后一个ACK成功到达服务器(若丢失,服务器会重传FIN)。
- 让旧连接的所有包在网络中自然消亡,避免影响新连接。
-
异常处理
- 若客户端在
TIME_WAIT超时前收到服务器重传的FIN,会重新发送ACK并重置计时器。 - 若服务器未收到ACK,会重传FIN(默认重试8次,约120秒)。
- 若客户端在
四次挥手是TCP可靠性的终极体现:
- 通过四次消息交换,实现双向连接的有序关闭。
- 通过状态机和超时机制,确保任何异常都能被妥善处理。
- 通过TIME_WAIT状态,从根本上杜绝了新旧连接的数据包混淆。