TCP 的状态可以分为「连接建立、数据传输、连接释放」三个阶段,下文按完整流程拆解每个状态的核心含义、触发场景与流转逻辑:

1. 连接建立阶段(三次握手)

这一阶段的核心目标是让客户端和服务器同步序列号,建立可靠的TCP通信连接。
| 状态 | 含义 | 典型场景 |
|---|---|---|
LISTEN |
服务器监听指定端口,处于空闲等待状态,随时接收客户端的连接请求 | 服务器调用listen()系统函数后,进入该稳定监听状态 |
SYN_SENT |
客户端已发送SYN握手包(第一次握手),等待服务器返回确认报文 | 客户端调用connect()发起连接请求后,立即进入该状态 |
SYN_RCVD |
服务器成功接收客户端的SYN包,且已回复SYN+ACK报文(第二次握手),等待客户端最终ACK确认 | 服务器监听到客户端SYN连接请求并完成二次握手响应后,进入该状态 |
ESTABLISHED |
三次握手流程全部完成,TCP连接正式建立,双方可正常收发数据 | 客户端收到SYN+ACK后回复ACK、服务器收到该ACK报文,双方同步进入该状态 |
补充:存在极少的「同时打开」特殊场景------客户端与服务器同时向对方发送SYN包,双方均会进入SYN_RCVD状态,互相回复SYN+ACK后,最终同步进入ESTABLISHED状态。
2. 数据传输阶段
本阶段仅有一个核心状态:ESTABLISHED
该状态下TCP连接处于活跃可用状态,通信双方无主次区分,可双向自由发送、接收数据,是TCP连接的核心工作状态。
3. 连接释放阶段(四次挥手)

(服务器发送的FIN包,默认携带ACK标志位,因此第三次挥手的报文自带确认属性)
TCP为全双工通信协议,双向数据通道需分别关闭,因此连接释放需要四次报文交互,即「四次挥手」。核心需区分主动关闭方 和被动关闭方的不同状态流转流程:
(1)被动关闭方(典型为服务器,图中右侧虚线框)
客户端主动发起连接关闭时,服务器的完整状态流转:
- ESTABLISHED → 收到客户端FIN包(第一次挥手)、回复ACK确认(第二次挥手) → 进入
CLOSE_WAIT状态。
CLOSE_WAIT:服务器已感知客户端主动关闭单向通道,但自身可能仍有未传输完成的数据,需等待应用层调用close()主动关闭连接。
-
应用层执行
close(),发送FIN关闭包(第三次挥手) → 进入LAST_ACK状态。 -
收到客户端的最终ACK确认包(第四次挥手) → 进入
CLOSED状态,连接彻底释放。
(2)主动关闭方(典型为客户端,图中左侧虚线框)
客户端主动发起连接关闭的完整状态流转:
-
ESTABLISHED → 应用层调用
close(),发送FIN关闭包(第一次挥手) → 进入FIN_WAIT_1状态。 -
收到服务器的ACK确认包(第二次挥手) → 进入
FIN_WAIT_2状态,持续等待服务器的FIN关闭包。 -
收到服务器的FIN关闭包(第三次挥手),回复ACK确认包(第四次挥手) → 进入
TIME_WAIT状态。
TIME_WAIT:固定等待 2MSL(最长报文寿命的2倍),核心目的是确保服务器成功接收最终ACK包,同时拦截链路中残留的旧数据包,避免干扰新连接。
- 2MSL计时结束 → 进入
CLOSED状态,连接彻底关闭。
close()CLOSING 补充:存在「同时关闭」特殊场景------通信双方同时调用发送FIN包,互相回复ACK后进入 状态,收到对方ACK后转入TIME_WAIT状态,最终完成连接关闭。
客户端 + 服务器 完整通信流程(典型HTTP请求场景)
结合流程主线,完整梳理一次标准HTTP请求的TCP状态全流转:
-
服务器:
CLOSED→ 执行socket()创建套接字→bind()绑定端口→listen()开启监听 → 进入LISTEN状态(监听业务端口)。 -
客户端:
CLOSED→ 执行socket()创建套接字→connect()发起连接→发送SYN包 → 进入SYN_SENT状态。 -
服务器接收客户端SYN包,回复SYN+ACK报文 → 进入
SYN_RCVD状态。 -
客户端接收SYN+ACK报文,回复ACK确认 → 进入
ESTABLISHED状态,开始发送HTTP请求数据。 -
服务器接收客户端ACK报文 → 同步进入
ESTABLISHED状态,处理客户端请求并准备响应数据。 -
客户端接收服务端响应数据,调用
close()关闭连接,发送FIN包 → 进入FIN_WAIT_1状态。 -
服务器接收FIN包,回复ACK确认 → 进入
CLOSE_WAIT状态,处理剩余未完成数据。 -
客户端接收服务器ACK包 → 进入
FIN_WAIT_2状态,等待服务器FIN关闭包。 -
服务器处理完所有数据,调用
close()发送FIN包 → 进入LAST_ACK状态。 -
客户端接收服务器FIN包,回复ACK确认 → 进入
TIME_WAIT状态,启动2MSL计时。 -
服务器接收最终ACK包 → 进入
CLOSED状态,服务端连接立即关闭。 -
客户端2MSL计时结束 → 进入
CLOSED状态,本次TCP连接彻底销毁。
常见异常状态问题分析
-
CLOSE_WAIT数量过多 :说明被动关闭方(多为服务器)已收到对方FIN关闭请求,但应用层代码未及时调用close()释放连接,大概率是程序逻辑漏洞,会导致连接堆积、系统资源占用过高。 -
TIME_WAIT数量过多:主动关闭方(多为客户端)关闭连接后必然进入该状态并等待2MSL。若服务器作为主动关闭方,大量TIME_WAIT状态会持续占用端口资源,导致端口耗尽、服务性能下降,可通过调整Linux内核参数优化缓解。
TIME_WAIT 状态深度解析
客户端在收到服务器的结束报文段后,不会直接进入CLOSED状态,而是转入TIME_WAIT状态,固定等待2MSL(报文段最大生存时间)时长后,才会彻底关闭连接。
TIME_WAIT 状态存在的核心意义有两点:
-
可靠终止整条TCP连接,避免连接残留异常;
-
确保链路中延迟滞留的旧TCP报文段被完全识别、丢弃,避免干扰新连接。
第一点可靠终止连接:若客户端回复的最终ACK报文丢失,服务器会超时重发FIN结束报文。客户端停留在TIME_WAIT状态,可及时接收重发的FIN包并重新发送ACK确认。若无该状态,客户端会直接释放连接,以复位报文回应服务器,服务器会判定连接异常报错,无法正常关闭连接。
第二点规避旧报文干扰:Linux系统中,同一TCP端口无法被重复复用。若没有TIME_WAIT状态,新程序可立即复用刚关闭连接的相同IP+端口,创建「连接化身」。此时链路中滞留的旧连接延迟报文,会被新连接接收,导致数据错乱、通信异常。TIME_WAIT的2MSL等待时长,可确保所有旧连接报文彻底失效、被网络丢弃。
TIME_WAIT状态会占用端口资源,导致程序无法立即重启:服务进程退出后,残留的TIME_WAIT连接会占用对应端口,新进程无法绑定端口启动,需等待2MSL超时释放。
客户端通常使用系统随机分配的临时端口建立连接,重启后大概率不会复用旧端口,因此客户端程序一般可立即重启,不受TIME_WAIT影响。
而服务器固定使用知名端口对外提供服务,若服务器主动关闭连接、残留TIME_WAIT状态,会导致端口被占用,无法立即重启服务。可通过配置Socket选项 SO_REUSEADDR,强制进程复用处于TIME_WAIT状态的端口,实现服务快速重启。
四次挥手中,主动发送FIN包发起关闭、且最后回复ACK确认对方FIN的一方,才会进入TIME_WAIT状态、等待2MSL时长。
核心总结:
-
率先调用
close()发送FIN包的一方,为主动关闭方; -
主动关闭方接收对方FIN包、回复ACK后,必然进入TIME_WAIT状态;
-
被动关闭方仅在收到FIN后响应ACK,最终接收对方ACK后直接进入CLOSED状态,不会产生TIME_WAIT。