TCP 连接状态由 RFC 793 定义,共 11 种标准状态。以下按连接生命周期详细解释:
1. 连接建立阶段
LISTEN (监听)
-
状态值 :
0A(十六进制)/10(十进制) -
含义:服务器端等待客户端的连接请求
-
触发条件 :调用
listen()系统调用后 -
查看方法 :
bashss -lnt # 查看所有监听端口 netstat -lnt -
典型场景 :
bash# Web服务器监听 Proto Recv-Q Send-Q Local Address:Port Peer Address:Port tcp 0 0 *:80 *:* LISTEN
SYN-SENT (同步已发送)
-
状态值 :
02/2 -
含义:客户端发送 SYN 后等待服务器回复
-
触发条件 :调用
connect()后,发送 SYN 包 -
持续时间:通常很短(RTT + 处理时间)
-
问题诊断 :
bash# 大量SYN-SENT可能表示: # 1. 服务器未响应 # 2. 防火墙阻止 ss -ant | grep -c SYN-SENT
SYN-RECEIVED (同步已接收)
-
状态值 :
03/3 -
别名:SYN-RCVD
-
含义:服务器收到 SYN,发送 SYN+ACK 后等待客户端 ACK
-
触发条件:服务器收到 SYN,发送 SYN+ACK 后
-
半连接队列 :处于此状态的连接在半连接队列(SYN Queue)
-
安全注意 :SYN Flood 攻击的目标状态
-
调优参数 :
bashsysctl net.ipv4.tcp_max_syn_backlog # 半连接队列大小 sysctl net.ipv4.tcp_syncookies # 防御SYN Flood
2. 连接已建立阶段
ESTABLISHED (已建立连接)
-
状态值 :
01/1 -
含义:连接已建立,可以正常传输数据
-
触发条件:完成三次握手后
-
全连接队列 :从 SYN-RCVD 移入全连接队列(Accept Queue)
-
查看详细 :
bash# 查看所有已建立连接 ss -nt # 按进程查看 ss -ntp -
数据传输 :
lua客户端 服务器 | ESTABLISHED | | <-------------------------------> | | 数据自由流动 |
3. 连接关闭阶段(正常关闭)
FIN-WAIT-1 (结束等待1)
-
状态值 :
04/4 -
含义:主动关闭方发送 FIN 后,等待对方 ACK
-
触发条件 :调用
close()或shutdown(SHUT_WR) -
可能转换 :
scssFIN-WAIT-1 → FIN-WAIT-2 # 收到ACK FIN-WAIT-1 → CLOSING # 同时收到FIN(同时关闭) FIN-WAIT-1 → TIME-WAIT # 收到FIN+ACK
FIN-WAIT-2 (结束等待2)
-
状态值 :
05/5 -
含义:收到对方对 FIN 的 ACK,等待对方发送 FIN
-
持续时间:可保持较长时间(对方还未调用 close)
-
查看 :
bash# 查看处于此状态的连接 ss -ant | grep FIN-WAIT-2 -
问题 :过多连接可能导致资源耗尽
bashsysctl net.ipv4.tcp_fin_timeout # 默认60秒
CLOSE-WAIT (关闭等待)
-
状态值 :
08/8 -
含义:被动关闭方收到 FIN 后,等待应用层关闭
-
触发条件:收到对方的 FIN,发送 ACK 后
-
关键问题 :应用层未及时关闭连接 会导致大量 CLOSE-WAIT
bash# 排查方法 ss -ant | grep CLOSE-WAIT # 找到对应进程 ss -antp | grep CLOSE-WAIT # 常见原因:未调用 close(),连接泄漏
CLOSING (正在关闭)
-
状态值 :
0B/11 -
含义 :双方同时尝试关闭连接(罕见)
-
触发条件 :
lua双方几乎同时发送FIN 客户端: FIN-WAIT-1 --发送FIN--> 服务器 服务器: FIN-WAIT-1 --发送FIN--> 客户端 双方收到对方FIN后进入CLOSING -
查看 :
bash# 通常很少见 ss -ant | grep -c CLOSING
LAST-ACK (最后确认)
- 状态值 :
09/9 - 含义:被动关闭方发送自己的 FIN 后,等待最后 ACK
- 触发条件:CLOSE-WAIT 状态的应用调用 close() 后
- 转换:收到 ACK 后进入 CLOSED
TIME-WAIT (时间等待)
-
状态值 :
06/6 -
含义:主动关闭方收到对方的 FIN 并发送 ACK 后
-
持续时间 :2MSL (Maximum Segment Lifetime)
- 默认:
60秒(Linux) - 查看:
sysctl net.ipv4.tcp_fin_timeout
- 默认:
-
目的 :
- 确保对方收到最后的 ACK
- 让旧连接的数据包在网络中消失
-
查看和管理 :
bash# 统计数量 ss -ant | grep -c TIME-WAIT # 查看详细信息 ss -ant state time-wait # 调优参数 sysctl net.ipv4.tcp_tw_reuse # 重用TIME-WAIT连接 sysctl net.ipv4.tcp_tw_recycle # 快速回收(谨慎使用) sysctl net.ipv4.tcp_max_tw_buckets # 限制数量
4. 特殊/终止状态
CLOSED (关闭)
- 状态值 :
07/7 - 含义:连接完全关闭,无资源占用
- 注意:统计中通常看不到,因为已释放资源
状态转换图详解
sql
+---------+
| CLOSED |
+---------+
被动打开 | 主动打开
LISTEN | SYN-SENT
| | |
v | |
+---------+ | |
| LISTEN | | |
+---------+ | |
收到SYN | | |
发送SYN+ACK | | |
| | |
v | |
+-----------+ | |
|SYN-RECEIVED|<---|--------+
+-----------+ | 收到ACK
收到ACK | | |
| | |
v | |
+---------+ | |
|ESTABLISHED|<--+--------+
+---------+ 连接建立完成
| |
| 应用数据传输 |
| |
| 主动关闭 被动关闭
| 发送FIN 收到FIN
| 发送ACK
v |
+---------+ +-----------+
|FIN-WAIT-1| | CLOSE-WAIT|
+---------+ +-----------+
| |
收到ACK | |应用调用close
v |发送FIN
+---------+ +---------+
|FIN-WAIT-2| |LAST-ACK |
+---------+ +---------+
| |
收到FIN | |收到ACK
发送ACK | |
v v
+---------+ +---------+
|TIME-WAIT| | CLOSED |
+---------+ +---------+
| 等待2MSL
v
+---------+
| CLOSED |
+---------+
完整查看脚本
bash
#!/bin/bash
# tcp_state_detailed.sh
# 查看详细的TCP状态统计
echo "================================================"
echo " TCP 连接状态详细统计"
echo "================================================"
echo ""
# 使用 ss 查看(推荐)
echo "【方法1: ss 命令】"
echo "状态 数量 描述"
echo "-----------------------"
ss -ant | awk 'NR>1 {print $2}' | sort | uniq -c | while read count state; do
case $state in
LISTEN) desc="监听端口,等待连接" ;;
SYN-SENT) desc="客户端已发送SYN" ;;
SYN-RECV) desc="服务器收到SYN,已回复SYN+ACK" ;;
ESTAB) desc="连接已建立,数据传输中" ;;
FIN-WAIT-1) desc="主动关闭方发送FIN,等待ACK" ;;
FIN-WAIT-2) desc="收到FIN的ACK,等待对方FIN" ;;
TIME-WAIT) desc="等待2MSL,确保连接完全关闭" ;;
CLOSED) desc="连接完全关闭" ;;
CLOSE-WAIT) desc="被动关闭方收到FIN,等待应用关闭" ;;
LAST-ACK) desc="被动关闭方发送FIN,等待最后ACK" ;;
CLOSING) desc="双方同时尝试关闭" ;;
UNKNOWN) desc="未知状态" ;;
*) desc="" ;;
esac
printf "%-12s %6d %s\n" "$state" "$count" "$desc"
done
echo ""
echo "【方法2: /proc/net/tcp 原始数据】"
echo "状态(十六进制) 数量"
echo "-------------------"
awk '{print $4}' /proc/net/tcp | awk -F: '{print $1}' | sort | uniq -c | sort -rn | while read count hex_state; do
case $hex_state in
0A) state="LISTEN" ;;
01) state="ESTABLISHED" ;;
02) state="SYN_SENT" ;;
03) state="SYN_RECV" ;;
04) state="FIN_WAIT1" ;;
05) state="FIN_WAIT2" ;;
06) state="TIME_WAIT" ;;
07) state="CLOSE" ;;
08) state="CLOSE_WAIT" ;;
09) state="LAST_ACK" ;;
0B) state="CLOSING" ;;
*) state="UNKNOWN($hex_state)" ;;
esac
printf "%-10s %6s %6d\n" "$hex_state" "$state" "$count"
done
echo ""
echo "【关键指标监控】"
total=$(ss -ant | grep -c -v State)
echo "总TCP连接数: $total"
echo ""
# 监控建议
echo "【健康检查建议】"
close_wait=$(ss -ant state close-wait | grep -c -v State)
if [ $close_wait -gt 100 ]; then
echo "⚠️ 警告: CLOSE-WAIT 过多($close_wait),可能存在连接泄漏"
echo " 排查方法:"
echo " 1. ss -antp state close-wait"
echo " 2. 检查对应应用的连接关闭逻辑"
fi
time_wait=$(ss -ant state time-wait | grep -c -v State)
if [ $time_wait -gt 10000 ]; then
echo "⚠️ 警告: TIME-WAIT 过多($time_wait),可能影响新连接"
echo " 优化建议:"
echo " 1. sysctl net.ipv4.tcp_tw_reuse=1"
echo " 2. sysctl net.ipv4.tcp_max_tw_buckets=2000000"
fi
状态诊断与调优
常见问题及解决
问题1:大量 TIME-WAIT
bash
# 原因:短连接过多
# 解决:
sysctl -w net.ipv4.tcp_tw_reuse=1 # 重用
sysctl -w net.ipv4.tcp_tw_recycle=1 # 回收(NAT环境慎用)
sysctl -w net.ipv4.tcp_max_tw_buckets=2000000
问题2:大量 CLOSE-WAIT
bash
# 原因:应用未正确关闭连接
# 排查:
lsof -nP -iTCP -sTCP:CLOSE_WAIT
# 或
ss -antp state close-wait
# 检查对应进程的代码
问题3:大量 SYN-RECV
bash
# 原因:可能遭受SYN Flood攻击
# 防御:
sysctl -w net.ipv4.tcp_syncookies=1
sysctl -w net.ipv4.tcp_max_syn_backlog=4096
sysctl -w net.ipv4.tcp_synack_retries=2
状态监控工具
实时监控脚本
bash
#!/bin/bash
# 实时监控TCP状态变化
watch -n 1 '
echo "TCP状态实时监控 - $(date)"
echo "----------------------------------------"
ss -ant | awk '\''NR>1 {count[$2]++} END {
states["LISTEN"]="监听";
states["SYN-SENT"]="发SYN";
states["SYN-RECV"]="收SYN";
states["ESTAB"]="已连接";
states["FIN-WAIT-1"]="等FIN-ACK";
states["FIN-WAIT-2"]="等对方FIN";
states["TIME-WAIT"]="等2MSL";
states["CLOSE-WAIT"]="等应用关";
states["LAST-ACK"]="等最后ACK";
states["CLOSING"]="同时关闭";
for(s in count) {
printf "%-12s %6d %s\n", s, count[s], states[s]
}
}'\'' | sort
'