TCP 状态机详解

概述

TCP 是一种面向连接的协议,通过三次握手建立连接、四次挥手断开连接。TCP 状态机描述了连接从建立到断开的完整生命周期,理解状态机是掌握 TCP 协议的关键。


一、TCP 状态机全景图

1.1 状态转换流程描述

**连接建立阶段(三次握手):**

  1. CLOSED → SYN_SENT:客户端主动打开,发送 SYN

  2. SYN_SENT → ESTABLISHED:收到服务器的 SYN+ACK,发送 ACK

  3. CLOSED → LISTEN:服务器被动打开,等待连接

  4. LISTEN → SYN_RCVD:收到客户端的 SYN,发送 SYN+ACK

  5. SYN_RCVD → ESTABLISHED:收到客户端的 ACK

**数据传输阶段:**

  • ESTABLISHED:双向数据传输状态

**连接断开阶段(四次挥手):**

  1. ESTABLISHED → FIN_WAIT_1:主动关闭方发送 FIN

  2. FIN_WAIT_1 → FIN_WAIT_2:收到对方的 ACK

  3. FIN_WAIT_2 → TIME_WAIT:收到对方的 FIN,发送 ACK

  4. TIME_WAIT → CLOSED:等待 2MSL 超时后关闭

  5. ESTABLISHED → CLOSE_WAIT:被动关闭方收到 FIN,发送 ACK

  6. CLOSE_WAIT → LAST_ACK:发送 FIN

  7. LAST_ACK → CLOSED:收到最终 ACK

1.2 状态转换表

| 当前状态 | 事件 | 下一个状态 | 动作 |

|---------|------|-----------|------|

| CLOSED | 主动打开 | SYN_SENT | 发送 SYN |

| CLOSED | 被动打开 | LISTEN | 等待连接 |

| LISTEN | 收到 SYN | SYN_RCVD | 发送 SYN+ACK |

| SYN_SENT | 收到 SYN | SYN_RCVD | 发送 SYN+ACK |

| SYN_SENT | 收到 SYN+ACK | ESTABLISHED | 发送 ACK |

| SYN_RCVD | 收到 ACK | ESTABLISHED | - |

| ESTABLISHED | 主动关闭 | FIN_WAIT_1 | 发送 FIN |

| ESTABLISHED | 被动关闭 | CLOSE_WAIT | 收到 FIN |

| FIN_WAIT_1 | 收到 FIN+ACK | FIN_WAIT_2 | - |

| FIN_WAIT_1 | 收到 ACK | FIN_WAIT_1 | 等待 FIN |

| FIN_WAIT_2 | 收到 FIN | TIME_WAIT | 发送 ACK |

| CLOSE_WAIT | 主动关闭 | LAST_ACK | 发送 FIN |

| LAST_ACK | 收到 ACK | CLOSED | - |

| TIME_WAIT | 2MSL 超时 | CLOSED | - |


二、各状态详解

2.1 LISTEN(监听状态)

**定义:** 服务器端等待客户端连接请求的状态。

**场景:**

```

服务器启动后,调用 listen() 函数:

socket() → bind() → listen()

此时服务器进入 LISTEN 状态,等待客户端的 SYN 包。

```

**典型操作:**

```bash

Linux 查看 LISTEN 状态的连接

netstat -anp | grep LISTEN

ss -tlnp

示例输出

tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 1234/nginx

```

2.2 SYN_SENT(同步已发送)

**定义:** 客户端主动发起连接,发送 SYN 包后的状态。

**场景:**

```

客户端调用 connect() 函数:

  1. 发送 SYN 包(包含初始序列号 ISN)

  2. 进入 SYN_SENT 状态

  3. 等待服务器的 SYN+ACK 响应

时间线:

T0: 客户端发送 SYN(SYN, seq=1000)

T1: 等待服务器响应...

```

**典型问题:**

  • 如果长时间停留在 SYN_SENT,可能是:

  • 服务器未启动或端口未开放

  • 网络不通

  • 防火墙阻止

2.3 SYN_RCVD(同步已接收)

**定义:** 服务器收到客户端 SYN 包,发送 SYN+ACK 后的状态。

**场景:**

```

服务器处理连接请求:

  1. 收到 SYN(SYN, seq=1000)

  2. 发送 SYN+ACK(SYN+ACK, seq=2000, ack=1001)

  3. 进入 SYN_RCVD 状态

  4. 等待客户端的 ACK 确认

三次握手的第二阶段完成。

```

**SYN 攻击防护:**

```

SYN_RCVD 状态是 SYN 洪水攻击的目标。

防护措施:

  • SYN Cookie

  • 限制半开连接数

  • 缩短超时时间

```

2.4 ESTABLISHED(已建立连接)

**定义:** 连接已完全建立,双方可以进行数据传输。

**场景:**

```

三次握手完成:

客户端:SYN → SYN+ACK → ACK(进入 ESTABLISHED)

服务器:SYN → SYN+ACK → ACK(进入 ESTABLISHED)

此时可以发送和接收数据:

send() / recv() 操作都在这个状态进行。

```

**数据传输示例:**

```

客户端发送数据:

SYN\] → \[SYN+ACK\] → \[ACK\] → \[DATA, seq=1001, len=100\] → \[ACK, ack=1101

服务器接收数据:

SYN\] → \[SYN+ACK\] → \[ACK\] → \[DATA, seq=1001\] → \[ACK

```

2.5 FIN_WAIT_1(终止等待1)

**定义:** 主动关闭方发送 FIN 包后的状态,等待对方的 FIN+ACK 或 ACK。

**场景:**

```

客户端决定关闭连接:

  1. 调用 close() 或 shutdown(SHUT_WR)

  2. 发送 FIN(FIN, seq=1101)

  3. 进入 FIN_WAIT_1 状态

  4. 等待服务器响应

```

**可能的转换:**

  • 收到 FIN+ACK → FIN_WAIT_2

  • 收到 ACK → 仍在 FIN_WAIT_1(等待 FIN)

2.6 FIN_WAIT_2(终止等待2)

**定义:** 主动关闭方收到对方的 ACK(但未收到 FIN)后的状态。

**场景:**

```

客户端流程:

  1. 发送 FIN

  2. 收到 ACK(服务器确认收到 FIN)

  3. 进入 FIN_WAIT_2 状态

  4. 等待服务器的 FIN 包

此时客户端不能发送数据,但可以接收数据。

```

**异常情况:**

  • 如果服务器长时间不发送 FIN,客户端会一直停留在 FIN_WAIT_2

  • 可以设置 `tcp_fin_timeout` 超时时间

2.7 CLOSE_WAIT(关闭等待)

**定义:** 被动关闭方收到对方 FIN 后的状态。

**场景:**

```

服务器收到客户端的 FIN:

  1. 收到 FIN(FIN, seq=1101)

  2. 发送 ACK(ACK, ack=1102)

  3. 进入 CLOSE_WAIT 状态

  4. 应用层决定是否关闭

此时服务器需要通知应用层:"对方要关闭连接了"

```

**关键操作:**

```

应用层收到 EOF 通知后,应该:

  1. 完成剩余数据发送

  2. 调用 close() 关闭连接

  3. 发送 FIN 给对方

```

2.8 LAST_ACK(最后确认)

**定义:** 被动关闭方发送 FIN 后的状态,等待最后的 ACK。

**场景:**

```

服务器关闭连接:

  1. 应用层调用 close()

  2. 发送 FIN(FIN, seq=2201)

  3. 进入 LAST_ACK 状态

  4. 等待客户端的 ACK

收到 ACK 后,连接完全关闭。

```

2.9 TIME_WAIT(时间等待)

**定义:** 主动关闭方收到对方 FIN 并发送 ACK 后的状态,等待 2MSL 时间。

**场景:**

```

客户端收到服务器的 FIN:

  1. 收到 FIN(FIN, seq=2201)

  2. 发送 ACK(ACK, ack=2202)

  3. 进入 TIME_WAIT 状态

  4. 等待 2MSL(两倍最大段生命周期)

2MSL 通常为 1-4 分钟(Linux 默认 60 秒)

```

**TIME_WAIT 的作用:**

```

  1. 确保最后的 ACK 能到达对方
  • 如果 ACK 丢失,对方会重传 FIN

  • TIME_WAIT 期间可以重新发送 ACK

  1. 防止旧连接的分段干扰新连接
  • 网络中可能存在延迟的数据包

  • 等待足够时间让它们过期

```

**TIME_WAIT 过多问题:**

```bash

查看 TIME_WAIT 数量

netstat -s | grep TIME_WAIT

优化参数

sysctl -w net.ipv4.tcp_tw_reuse=1 # 复用 TIME_WAIT 端口

sysctl -w net.ipv4.tcp_tw_recycle=1 # 快速回收

sysctl -w net.ipv4.tcp_fin_timeout=30 # 缩短超时时间

```


三、状态转换实例分析

3.1 正常连接建立(三次握手)

```

客户端 服务器

│ │

│----- SYN(seq=1000) ----------→│ CLOSED → LISTEN → SYN_RCVD

│ │

│←--- SYN+ACK(seq=2000, │

│ ack=1001) ----------------│

│ │

│----- ACK(ack=2001) ----------→│ SYN_RCVD → ESTABLISHED

│ │

▼ ▼

SYN_SENT → ESTABLISHED LISTEN → SYN_RCVD → ESTABLISHED

状态转换:

客户端:CLOSED → SYN_SENT → ESTABLISHED

服务器:CLOSED → LISTEN → SYN_RCVD → ESTABLISHED

```

3.2 正常连接断开(四次挥手)

```

客户端(主动关闭) 服务器(被动关闭)

│ │

│----- FIN(seq=1500) ----------→│ ESTABLISHED → CLOSE_WAIT

│ │

│←--- ACK(ack=1501) -----------│

│ │

▼ │

FIN_WAIT_1 → FIN_WAIT_2 │

│ │

│←--- FIN(seq=2500) -----------│ CLOSE_WAIT → LAST_ACK

│ │

│----- ACK(ack=2501) ----------→│ LAST_ACK → CLOSED

│ │

FIN_WAIT_2 → TIME_WAIT → CLOSED

状态转换:

客户端:ESTABLISHED → FIN_WAIT_1 → FIN_WAIT_2 → TIME_WAIT → CLOSED

服务器:ESTABLISHED → CLOSE_WAIT → LAST_ACK → CLOSED

```

3.3 同时关闭

```

客户端 服务器

│ │

│----- FIN(seq=1500) ----------→│

│←--- FIN(seq=2500) -----------│

│ │

│----- ACK(ack=2501) ----------→│

│←--- ACK(ack=1501) -----------│

│ │

▼ ▼

TIME_WAIT TIME_WAIT

双方同时进入 TIME_WAIT 状态。

```


四、状态异常排查

4.1 常见异常状态

| 异常状态 | 可能原因 | 排查方向 |

|---------|---------|---------|

| SYN_SENT 过多 | 连接无法建立 | 检查目标端口、防火墙、网络连通性 |

| SYN_RCVD 过多 | SYN 攻击或半开连接 | 检查 SYN Cookie、连接限制 |

| FIN_WAIT_2 过多 | 被动关闭方未发送 FIN | 检查应用层是否正确关闭连接 |

| CLOSE_WAIT 过多 | 应用层未及时 close() | 检查代码逻辑、资源泄漏 |

| TIME_WAIT 过多 | 短连接频繁建立关闭 | 优化参数、使用连接池 |

4.2 排查工具

**netstat / ss 命令:**

```bash

查看所有状态

netstat -anp | grep tcp

统计各状态数量

netstat -anp | grep tcp | awk '{print $6}' | sort | uniq -c

查看特定状态

ss -t4 state TIME-WAIT

ss -t4 state FIN-WAIT-1

```

**tcpdump 抓包分析:**

```bash

抓包分析三次握手

tcpdump -i eth0 port 80 -nn -vv

过滤特定状态的包

tcpdump -i eth0 'tcp[tcpflags] & (tcp-syn|tcp-fin) != 0' -nn

```

4.3 典型问题案例

**案例1:CLOSE_WAIT 过多**

```

现象:服务器有大量 CLOSE_WAIT 状态连接

原因:应用层没有及时调用 close()

排查步骤:

  1. 确认进程:netstat -anp | grep CLOSE_WAIT | head -5

  2. 检查代码:是否有未关闭的 socket

  3. 查看日志:是否有异常退出

  4. 使用 lsof:lsof -p <pid> | grep TCP

```

**案例2:SYN_SENT 无法建立连接**

```

现象:客户端一直停留在 SYN_SENT

原因:服务器端口未开放或防火墙阻止

排查步骤:

  1. ping 测试网络连通性

  2. telnet 测试端口:telnet server_ip port

  3. 检查服务器防火墙:iptables -L

  4. 检查服务器服务状态:systemctl status service

```

**案例3:TIME_WAIT 过多影响新连接**

```

现象:无法建立新连接,提示端口耗尽

原因:短连接导致 TIME_WAIT 占满端口

解决方法:

sysctl -w net.ipv4.tcp_tw_reuse=1

sysctl -w net.ipv4.ip_local_port_range="1024 65535"

使用长连接替代短连接

```


五、状态机编程实践

5.1 客户端状态机实现

```python

class TCPStateClient:

def init(self):

self.state = 'CLOSED'

def open(self):

if self.state == 'CLOSED':

self.state = 'SYN_SENT'

print("发送 SYN")

def on_syn_ack(self):

if self.state == 'SYN_SENT':

self.state = 'ESTABLISHED'

print("发送 ACK")

def close(self):

if self.state == 'ESTABLISHED':

self.state = 'FIN_WAIT_1'

print("发送 FIN")

def on_ack(self):

if self.state == 'FIN_WAIT_1':

self.state = 'FIN_WAIT_2'

def on_fin(self):

if self.state == 'FIN_WAIT_2':

self.state = 'TIME_WAIT'

print("发送 ACK")

def timeout(self):

if self.state == 'TIME_WAIT':

self.state = 'CLOSED'

```

5.2 服务器状态机实现

```python

class TCPStateServer:

def init(self):

self.state = 'CLOSED'

def listen(self):

if self.state == 'CLOSED':

self.state = 'LISTEN'

def on_syn(self):

if self.state == 'LISTEN':

self.state = 'SYN_RCVD'

print("发送 SYN+ACK")

def on_ack(self):

if self.state == 'SYN_RCVD':

self.state = 'ESTABLISHED'

def on_fin(self):

if self.state == 'ESTABLISHED':

self.state = 'CLOSE_WAIT'

print("发送 ACK")

def close(self):

if self.state == 'CLOSE_WAIT':

self.state = 'LAST_ACK'

print("发送 FIN")

def on_final_ack(self):

if self.state == 'LAST_ACK':

self.state = 'CLOSED'

```


六、总结

6.1 状态机核心要点

```

  1. 三次握手:CLOSED → SYN_SENT → ESTABLISHED(客户端)

CLOSED → LISTEN → SYN_RCVD → ESTABLISHED(服务器)

  1. 四次挥手:ESTABLISHED → FIN_WAIT_1 → FIN_WAIT_2 → TIME_WAIT → CLOSED(主动)

ESTABLISHED → CLOSE_WAIT → LAST_ACK → CLOSED(被动)

  1. TIME_WAIT 是关键状态,确保连接完全关闭

  2. CLOSE_WAIT 通常是应用层问题

```

6.2 状态异常处理原则

```

  1. 监控各状态数量变化

  2. 建立基线,识别异常

  3. 使用工具定位根因

  4. 根据具体状态采取对应措施

```

理解 TCP 状态机是网络编程和系统运维的基础,掌握状态转换规律有助于快速定位网络问题。

相关推荐
@encryption2 小时前
计算机网络 --- LSA
网络·计算机网络·智能路由器
谪星·阿凯3 小时前
内网渗透之权限维持:从域环境到单机的持久化控制指南
运维·服务器·网络·网络安全
宋浮檀s3 小时前
春秋云镜——CVE-2022-22965
网络·安全·web安全·网络安全
pengyi8710154 小时前
动态 IP 池调度算法核心原理:从随机分配到智能调度演进
网络·tcp/ip·算法
deepin_sir4 小时前
05 Chroma_高级检索:过滤、距离算法与元数据魔法
网络·数据库·算法
network_tester4 小时前
TSN网络流量及协议测试:为确定性网络护航的关键技术
网络·网络协议·车载系统·汽车·信息与通信·信号处理
卡不卡法4 小时前
如何评估IP查询工具的性能?4个核心指标+Python压测脚本
tcp/ip
天行健,君子而铎4 小时前
智识数据·合规赋能——知源-AI数据分类分级系统破解通用行业数据治理困局
大数据·网络·数据库
是Yu欸4 小时前
CC-Switch 零基础保姆级教程1(2026 最新版)
网络·人工智能·网络协议·http·大模型·claude·claude desktop
上海云盾-小余5 小时前
权限安全管控:账号权限分级与越权攻击防范
运维·网络·安全