TCP(传输控制协议)是一种面向连接、可靠的传输层协议,其状态转移机制是实现“三次握手”建立连接、“四次挥手”关闭连接及数据传输过程中异常处理的核心。理解 TCP 状态转移,需先明确 11 种核心状态,再围绕“连接建立-数据传输-连接关闭”三大阶段梳理状态变迁逻辑。

一、TCP 核心状态定义

首先明确每种状态的含义,这是理解状态转移的基础:

状态名称(英文)
状态名称(中文)
核心含义
CLOSED
关闭状态
TCP连接的初始/最终状态,无任何连接活动。
LISTEN
监听状态
服务器端处于“等待客户端连接请求”的状态(如调用`listen()`后)。
SYN_SENT
同步已发送
客户端发送连接请求(SYN报文)后,等待服务器确认的状态。
SYN_RCVD
同步已接收
服务器收到客户端SYN报文,回复SYN+ACK报文后,等待客户端最终确认的状态。
ESTABLISHED
已建立连接
客户端与服务器完成“三次握手”,连接正式建立,可进行数据传输的状态(核心工作状态)。
FIN_WAIT_1
终止等待1
主动关闭方(客户端/服务器均可)发送关闭请求(FIN报文)后,等待对方确认的状态。
FIN_WAIT_2
终止等待2
主动关闭方收到对方对FIN的确认(ACK报文)后,等待对方发送其自身FIN报文的状态。
CLOSE_WAIT
关闭等待
被动关闭方收到主动关闭方的FIN报文并回复ACK后,等待自身应用层“确认关闭”的状态(此时仍可接收数据)。
CLOSING
关闭中
双方同时发起关闭请求:主动关闭方发送FIN后,未收到ACK却先收到对方的FIN,此时进入该状态,等待对方ACK。
LAST_ACK
最后确认
被动关闭方发送自身的FIN报文后,等待主动关闭方确认(ACK)的状态。
TIME_WAIT
时间等待
主动关闭方收到被动关闭方的FIN报文并回复ACK后,不立即关闭,而是等待2MSL(报文最大生存时间,通常1-2分钟) 的状态(核心作用:避免延迟报文干扰新连接)。

二、TCP 状态转移的三大核心阶段

TCP 状态转移并非孤立,而是围绕“连接建立-数据传输-连接关闭”形成闭环,以下分阶段解析(结合经典场景:客户端主动发起连接,主动关闭连接)。

阶段 1:三次握手(建立连接)—— 从 CLOSED 到 ESTABLISHED

此阶段是客户端与服务器通过 3 次报文交互,确认双方“发送/接收能力正常”,最终进入可传输数据的 ESTABLISHED 状态。

步骤
发起方
状态变迁
核心动作(报文)
目的
1
客户端
CLOSED → SYN_SENT
发送SYN报文(同步序列编号,告诉服务器“我要连接,我的初始序号是X”)
发起连接请求,测试服务器接收能力
2
服务器
LISTEN → SYN_RCVD
回复SYN+ACK报文(告诉客户端“我收到你的请求,我的初始序号是Y,确认你的序号X”)
确认客户端请求,同时发起服务器的同步请求
3
客户端
SYN_SENT → ESTABLISHED
发送ACK报文(告诉服务器“我收到你的同步请求,确认你的序号Y”)
最终确认服务器请求,客户端先进入连接状态
4
服务器
SYN_RCVD → ESTABLISHED
收到客户端ACK报文
服务器确认客户端最终响应,双方进入连接状态

关键说明

  • 服务器需先通过 socket() 创建套接字,再调用 listen() 进入 LISTEN 状态,才能接收客户端连接;
  • 三次握手的核心是“避免因延迟的 SYN 报文建立无效连接”(如客户端旧 SYN 报文迟到,服务器若直接建立连接会浪费资源,三次握手可通过序号确认过滤无效请求)。

阶段 2:数据传输(连接维持)—— 稳定在 ESTABLISHED

双方进入 ESTABLISHED 状态后,可双向传输数据。此阶段状态不变,核心是通过“序号(Seq)”“确认号(Ack)”“滑动窗口”保证数据的可靠传输(如丢包重传、流量控制、拥塞控制)。

  • 若某一方需暂时停止传输,会发送“窗口大小=0”的报文,对方进入“窗口关闭”等待状态;一旦窗口重新开放,恢复数据传输,状态仍为 ESTABLISHED。

阶段 3:四次挥手(关闭连接)—— 从 ESTABLISHED 到 CLOSED

TCP 是“双向连接”,关闭时需双方分别确认“不再发送数据”,因此需 4 次报文交互(因被动关闭方可能仍有数据未发送,需先回复 ACK,再延迟发送 FIN)。

步骤
发起方(主动关闭方,如客户端)
被动关闭方(如服务器)
核心动作(报文)
状态变迁
1
客户端决定关闭连接
-
发送FIN报文(告诉服务器“我不再发数据了,你可以准备关闭”)
客户端:ESTABLISHED → FIN_WAIT_1

2
-
服务器收到FIN
回复ACK报文(告诉客户端“我收到你的关闭请求,会尽快处理”)
服务器:ESTABLISHED → CLOSE_WAIT
3
客户端收到ACK
服务器处理完剩余数据后

-
客户端:FIN_WAIT_1 → FIN_WAIT_2(等待服务器的FIN)
4
-
服务器发送FIN报文(告诉客户端“我也不再发数据了,你可以关闭”)
服务器:CLOSE_WAIT → LAST_ACK

5
客户端收到FIN
-

发送ACK报文(告诉服务器“我收到你的关闭请求,确认关闭”)
客户端:FIN_WAIT_2 → TIME_WAIT
6
客户端等待2MSL
服务器收到ACK
-
服务器:LAST_ACK → CLOSED(被动关闭方先完成关闭)
7
客户端2MSL超时
-

无报文,直接释放资源
客户端:TIME_WAIT → CLOSED(主动关闭方完成关闭)

关键说明

  1. 为什么需要四次挥手?因为 TCP 是双向通信,关闭时需分别关闭“客户端 → 服务器”和“服务器 → 客户端”两个方向的连接:

    • 第 1-2 步:关闭“客户端 → 服务器”的发送方向;
    • 第 3-4 步:关闭“服务器 → 客户端”的发送方向。若服务器仍有数据未发送,会在 CLOSE_WAIT 状态中处理完数据,再发送 FIN,因此无法像三次握手那样将“SYN+ACK”合并为一次报文。
  2. TIME_WAIT 状态的核心作用(最易误解的状态)

    • 确保被动关闭方收到最终的 ACK(若 ACK 丢失,服务器会重发 FIN,客户端在 TIME_WAIT 内可再次回复 ACK);
    • 避免延迟报文干扰新连接(2MSL 是报文在网络中最大生存时间,超时后网络中所有旧连接的报文都会失效,新连接不会被旧报文混淆)。
    • 注意:TIME_WAIT 是主动关闭方的必经状态,若服务器频繁主动关闭连接(如短连接服务),会积累大量 TIME_WAIT 连接,需通过内核参数(如 net.ipv4.tcp_tw_reuse)优化。

三、特殊状态转移场景

除了“正常三次握手/四次挥手”,还存在两种特殊场景,对应 CLOSING 状态:

场景:双方同时发起关闭请求

若客户端和服务器在 ESTABLISHED 状态下,同时向对方发送 FIN 报文:

  1. 客户端发送 FIN 后,进入 FIN_WAIT_1,未收到 ACK 却先收到服务器的 FIN → 客户端状态变为 CLOSING;
  2. 服务器发送 FIN 后,进入 FIN_WAIT_1,未收到 ACK 却先收到客户端的 FIN → 服务器状态变为 CLOSING;
  3. 双方各自收到对方的 FIN 后,回复 ACK;
  4. 双方收到 ACK 后,均进入 TIME_WAIT,等待 2MSL 后关闭。

这种场景极少发生,CLOSING 状态是临时过渡状态,最终仍会通过 TIME_WAIT 走向 CLOSED。

四、TCP 状态转移总结图(简化版)

为直观理解,可参考以下简化的状态转移路径(以客户端视角为主):

CLOSED → SYN_SENT → ESTABLISHED → FIN_WAIT_1 → FIN_WAIT_2 → TIME_WAIT → CLOSED

服务器视角的核心路径:

CLOSED → LISTEN → SYN_RCVD → ESTABLISHED → CLOSE_WAIT → LAST_ACK → CLOSED

五、常见问题与排查

  1. 大量 CLOSE_WAIT 状态的原因?
    被动关闭方(如服务器)收到 FIN 后,回复 ACK 进入 CLOSE_WAIT,但应用层未调用 close() 释放连接,导致状态无法进入 LAST_ACK。需排查应用层代码(如资源泄漏、未处理关闭事件)。

  2. 大量 TIME_WAIT 状态的影响?
    每个 TIME_WAIT 状态会占用一个端口(客户端端口),若短连接频繁建立/关闭,会导致端口耗尽。解决方案:

    • 开启 net.ipv4.tcp_tw_reuse(允许 TIME_WAIT 状态的端口被复用);
    • 调整 net.ipv4.tcp_tw_timeout(缩短 TIME_WAIT 超时时间,不建议小于 1 分钟)。
  3. SYN_RCVD 状态堆积的原因?
    服务器收到 SYN 后回复 SYN+ACK,但客户端未回复 ACK,导致服务器停留在 SYN_RCVD。可能原因:客户端异常退出、网络丢包、SYN 洪水攻击(需开启 TCP SYN Cookie 防御)。