Appearance
TCP 状态速查表
TCP 连接的状态是针对单个端点而言的。在一次完整的连接中,客户端和服务端会各自维护一份独立的 TCP 状态记录,因此在同一时刻,两端看到的状态很可能并不一致。在排障时,建议同时记录当前端点的角色(客户端/服务端)、网络四元组、状态持续时间、相关联的进程以及网络抓包数据。
状态总表
| 状态 | 当前含义 | 常见进入事件 | 常见退出事件 | 排障提示 |
|---|---|---|---|---|
| CLOSED | 当前没有任何活动的 TCP 连接状态 | 初始状态、连接释放完成、或失败后被清理 | 主动调用 connect 或被动开启监听 | 系统命令(如 netstat/ss)通常不会显示没有控制块的 CLOSED 状态;遇到 API 报错时,可通过抓包来补充排查失败原因 |
| LISTEN | 本地端点正在等待并接受发往该地址和端口的新连接 | bind 之后调用 listen | 应用主动关闭监听端点后释放资源;收到 SYN 报文时,会创建新连接并使其进入 SYN-RECEIVED 状态,原监听端点继续保持 LISTEN | 排障时优先核对绑定地址(如 0.0.0.0 等)、地址族、端口号、关联进程、队列溢出情况以及防火墙规则 |
| SYN-SENT | 主动方(通常是客户端)已发送 SYN 报文,正在等待对端的确认响应 | 阻塞或非阻塞调用 connect 发起连接 | 收到 SYN+ACK 后发送最终 ACK,随后进入 ESTABLISHED;超时或发生错误则被清理 | 如果卡在该状态的时间较长,需排查 SYN 重传情况、路由配置、ACL 策略、目标端是否在监听以及返回路径是否通畅 |
| SYN-RECEIVED | 已收到对端的 SYN 报文并回复了 SYN+ACK,正在等待对方的最终确认(ACK) | 处于被动打开状态(LISTEN)收到 SYN;“同时打开”时也可能进入此状态 | 收到有效的 ACK 后进入 ESTABLISHED;超时未收到确认则被内核回收 | 若出现大量积压,需重点检查握手完成率、半连接队列容量、是否触发 SYN Flood 防护机制以及网络返回路径是否正常 |
| ESTABLISHED | 双方已成功建立连接,可以进行双向数据传输 | 三次握手完成 | 本端主动关闭进入 FIN-WAIT-1;收到对端的 FIN 进入 CLOSE-WAIT;收到有效的 RST 会立即终止连接 | 长时间处于空闲状态可能是正常的连接池设计;需结合应用层心跳、代理超时配置与业务日志来综合判断连接是否依然健康 |
| FIN-WAIT-1 | 本端已发送 FIN 报文,正在等待对端的确认(ACK)或对端发来的 FIN | 本端主动调用 close 或 shutdown 结束发送通道 | 收到针对本端 FIN 的 ACK 后进入 FIN-WAIT-2;若同时收到对端的 FIN,则可能进入 CLOSING 或 TIME-WAIT | 若该状态持续堆积,需核对对端是否收到了 FIN、返回的 ACK 是否因网络原因丢失,以及本地是否有持续的 FIN 重传 |
| FIN-WAIT-2 | 本端的 FIN 已经被对端确认,正在等待对端发送 FIN 以结束其发送通道 | 在 FIN-WAIT-1 状态下收到了 ACK | 收到对端的 FIN 后,回复 ACK 并进入 TIME-WAIT | 长期卡在此状态通常意味着对端仍在发送数据,或者对端应用的关闭流程卡死了;注意内核通常会针对此类孤儿连接设置超时回收策略 |
| CLOSE-WAIT | 本端已收到对端的 FIN,但本地应用尚未关闭其发送通道 | 在 ESTABLISHED 状态下收到 FIN | 本地应用调用 close 完成关闭并发送 FIN 后,进入 LAST-ACK | 如果出现大量且长期的 CLOSE-WAIT,优先检查本地应用逻辑:是否正确处理了 EOF、线程是否死锁卡住、异常处理分支是否漏调了 Socket 的关闭方法 |
| CLOSING | 双方几乎同时发送了 FIN,且本端的 FIN 尚未收到确认 | 在 FIN-WAIT-1 状态下收到了对端的 FIN,并已对其回复了 ACK | 收到针对本端 FIN 的 ACK 后,进入 TIME-WAIT | 这种状态在实际中出现频率较低;通过网络抓包可以清晰确认双方 FIN 报文的交叉发送顺序 |
| LAST-ACK | 本端(作为被动关闭方)已发送 FIN,正在等待最后的 ACK 确认 | 在 CLOSE-WAIT 状态下,本地应用主动关闭了发送通道 | 收到最终的 ACK 后进入 CLOSED 状态;如果超时会重传 FIN 并最终由内核强制清理 | 若持续卡在此状态,需检查最后的 ACK 返回路径是否受阻、对端的当前状态,以及是否受到防火墙等中间设备的影响 |
| TIME-WAIT | 连接已关闭,但本端(主动关闭方)仍保留短期记录以处理网络中可能延迟到达的报文 | 主动关闭方确认了对端的 FIN;或者双方同时完成关闭 | 计时器(通常为 2MSL)到期后,彻底进入 CLOSED 状态 | 需重点观察连接的创建速率、可用临时端口范围、谁是主动关闭方以及对系统资源的实际影响;TIME-WAIT 数量多往往只是现象,不必盲目优化 |
主动建立路径
客户端(发起方)的常见状态流转路径:
text
CLOSED
│ connect,发送 SYN
▼
SYN-SENT
│ 收到 SYN+ACK,发送 ACK
▼
ESTABLISHED服务端(接收方)的监听端点始终保持在 LISTEN 状态。每当有新连接到来时,内核会为其维护独立的状态流转:
text
LISTEN
│ 收到 SYN,为新连接发送 SYN+ACK
▼
SYN-RECEIVED
│ 收到最终 ACK
▼
ESTABLISHED
│ 进入已完成连接队列,等待应用完成 accept
▼
应用持有已连接 Socket应用层调用 accept 会取得已经成功建立的连接。TCP 握手完成的时刻和应用层调用 accept 的时刻是解耦的,全连接队列在底层将这两个不同步的节奏连接了起来。
本端主动关闭路径
当本端率先结束发送通道(即主动触发断开连接)时,常见的状态流转如下:
text
ESTABLISHED
│ 本端发送 FIN
▼
FIN-WAIT-1
│ 对端确认本端 FIN
▼
FIN-WAIT-2
│ 对端稍后发送 FIN,本端回复 ACK
▼
TIME-WAIT
│ 计时器到期
▼
CLOSED值得注意的是,在进入 FIN-WAIT-2 阶段后,对端依然可以继续发送数据(半关闭状态)。此时本端的接收通道仍保持正常工作,直到最终收到对端发来的 FIN 报文,并将接收缓冲区中剩余的字节悉数交给应用层读取。
本端被动关闭路径
当本端率先收到对端发来的 FIN 报文(即被动断开连接)时,常见的状态流转如下:
text
ESTABLISHED
│ 收到 FIN,回复 ACK
▼
CLOSE-WAIT
│ 应用完成工作并关闭连接,发送 FIN
▼
LAST-ACK
│ 收到最终 ACK
▼
CLOSEDCLOSE-WAIT 状态的持续时间,很大程度上取决于本地应用的实现逻辑。当应用在读取操作中收到 EOF 后,它可以继续向对端发送完剩下的响应数据,随后再调用 shutdown 或 close 来彻底完成本方向的关闭流程。
同时关闭
如果双方在极其相近的时间点相互发送了 FIN 报文,可能会出现以下流转:
text
ESTABLISHED
│ 本端发送 FIN
▼
FIN-WAIT-1
│ 收到对端 FIN,先回复 ACK
▼
CLOSING
│ 收到本端 FIN 的 ACK
▼
TIME-WAIT在实际的网络抓包中,ACK 和 FIN 标志位经常会合并在同一个报文中出现(FIN+ACK),偶尔的重传也会使得实际报文数量多于理论值。因此,在分析状态迁移时,关注底层事件的核心语义(谁发送了数据、谁确认了状态),比死记硬背固定的报文数量要稳定得多。
半关闭与 EOF
调用 shutdown(SHUT_WR) 会关闭本端的发送通道,但接收通道依然保持开启。这意味着,对端在读完缓冲区中已有的字节后会收到一个 EOF,但本端依然可以继续读取对端随后发来的响应数据。
| 本端动作或事件 | 本端的发送通道 | 本端的接收通道 |
|---|---|---|
调用 shutdown(SHUT_WR) | 进入关闭流程并向外发送 FIN | 保持正常,依然可读 |
| 收到对端的 FIN | 仍可根据本地应用需要继续发送数据 | 读取完底层缓存数据后,向应用层报告 EOF |
调用完整的 close | 由底层协议栈负责完成关闭,或者按配置直接中止连接 | 应用层立即失去对该 Socket 句柄的控制权 |
RST 对状态的影响
一个有效的 RST 报文通常会立即终止当前的连接状态,并导致处于阻塞状态或后续调用的 Socket API 立即抛出错误。导致产生 RST 的常见场景包括:
- 访问的目标端口上没有任何进程在监听;
- 主机收到了属于某个已失效旧连接的报文,且当前系统中找不到匹配的连接状态;
- 应用层采取了中止式关闭(Abortive Close);
- 连接的某一方意外重启,丢失了原有的 TCP 状态机信息;
- 链路上的防火墙或中间网络设备主动切断并拒绝了该连接。
为了防止网络攻击,RST 报文是否被接受,还会受到当前连接状态以及各种 TCP 安全增强规范(如对序列号的严格校验)的约束。在分析此类问题时,务必记录下 RST 的实际发送方、Ack/Seq 字段、发生该事件前的前置操作,以及应用层具体的关闭方式。
常见状态组合
| 现象观察 | 首要排查方向 | 进一步佐证的线索 |
|---|---|---|
| 客户端处于 SYN-SENT,但服务端毫无记录 | 目标地址和端口是否正确、路由配置、防火墙策略,以及抓包点位置是否合理 | 两端同时抓包的比对结果、系统的路由表、服务端的监听状态 |
| 服务端 SYN-RECEIVED 状态大量积压 | 客户端的最终 ACK 是否未能返回、半连接队列是否溢出、是否遭受 SYN Flood 攻击 | 抓包统计 SYN/SYN+ACK/ACK 的比例、系统队列监控指标 |
| 服务端 CLOSE-WAIT 状态持续增长 | 应用层是否正确处理了 EOF、连接资源的释放逻辑是否有漏洞 | 应用程序的线程栈分析(排查阻塞)、连接对象的创建与关闭日志 |
| 客户端 FIN-WAIT-2 状态长期存在 | 了解对端应用的代码逻辑,排查其何时才会结束发送通道 | 对端的 TCP 状态表、对端应用日志、FIN 报文相关的抓包 |
| 系统存在海量的 TIME-WAIT | 业务短连接的并发速率、哪一方在主动关闭连接、临时端口资源是否枯竭 | 端口四元组的分布统计、系统可用临时端口范围、连接池的监控指标 |
| LAST-ACK 状态持续不消失 | 最终 ACK 的网络返回路径是否顺畅、对端目前处于什么状态 | 两端同时抓包、防火墙的会话超时设置、是否有 FIN 报文重传 |
排障记录模板
在进行排障并查询连接状态时,建议记录以下要素:
text
时间:
主机与网络命名空间:
进程/PID:
本地地址与端口:
远端地址与端口:
TCP 状态:
持续时间或首次观察时间:
同类连接数量:
对应日志事件:
对应抓包帧号:单次记录的状态快照仅仅代表某一特定时刻的切片。只有通过连续多次采样,才能真实揭示该连接是处于正常的流转中、稳定的空闲状态,还是由于某种异常而长期停滞在同一个阶段。