1. 为什么需要监控TCP连接?
在Linux服务器运维和网络问题排查中,TCP连接状态监控是每个系统管理员必须掌握的技能。当服务器出现性能瓶颈、网络异常或安全事件时,TCP连接数据往往能提供第一手线索。我曾在一次线上事故中,通过分析TCP连接状态快速定位到DDoS攻击源,这让我深刻认识到这项技能的重要性。
Linux内核通过虚拟文件系统暴露了大量网络栈信息,其中/proc/net/tcp和/proc/net/tcp6这两个伪文件实时记录了所有TCPv4和TCPv6连接的状态。与netstat、ss等工具相比,直接读取这些文件能获取更底层的信息,特别适合自动化监控和深度分析。
提示:/proc文件系统中的数据是动态生成的,每次读取都会获取最新状态,但频繁读取可能影响性能,在生产环境需谨慎。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. /proc/net/tcp文件格式解析
2.1 字段结构详解
/proc/net/tcp每行代表一个TCP连接,包含16个字段,以空格分隔。以下是关键字段的解析(以典型行sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode为例):
| 字段位置 | 示例值 | 含义说明 |
|---|---|---|
| 1 | 1 | 连接序号(SL),内核分配的标识号 |
| 2 | 010310AC:0050 | 本地地址和端口,十六进制表示。010310AC=10.3.1.16(小端序),0050=80端口 |
| 3 | 030310AC:8B1E | 远程地址和端口。030310AC=10.3.1.3,8B1E=35614端口 |
| 4 | 01 | 连接状态。01=ESTABLISHED,完整状态码见下文 |
| 5 | 00000000 | 发送队列中的数据量(字节) |
| 6 | 00000000 | 接收队列中的数据量(字节) |
| 7 | 00000000 | 定时器信息 |
| 8 | 8 | 从当前状态开始的jiffies数(1jiffy=10ms) |
| 9 | 0 | 超时重传次数 |
| 10 | 0 | 建立连接的超时时间 |
| 11 | 1000 | 用户ID(UID),启动该连接的进程所有者 |
2.2 TCP状态码对照表
状态字段(第4列)的十六进制值对应不同的TCP状态:
bash复制00: TCP_ESTABLISHED
01: TCP_SYN_SENT
02: TCP_SYN_RECV
03: TCP_FIN_WAIT1
04: TCP_FIN_WAIT2
05: TCP_TIME_WAIT
06: TCP_CLOSE
07: TCP_CLOSE_WAIT
08: TCP_LAST_ACK
09: TCP_LISTEN
0A: TCP_CLOSING
2.3 地址转换技巧
/proc/net/tcp中的IP地址采用十六进制小端序存储,需要特殊处理。例如:
- 原始值:010310AC
- 分割字节:01 03 10 AC
- 反转顺序:AC 10 03 01
- 转换十进制:172.16.3.1
端口号直接十六进制转十进制即可,如0050→80。
3. 实战:用AWK分析TCP连接
3.1 基础监控脚本
以下AWK脚本可以解析/proc/net/tcp并输出易读格式:
bash复制awk 'BEGIN {
printf "%-6s %-21s %-21s %-12s %-6s\n", "SL", "Local", "Remote", "State", "UID"
}
NR > 1 {
# 提取字段
split($2, local, ":");
split($3, remote, ":");
state = $4;
uid = $10;
# 转换IP地址
local_ip = convert_ip(local[1]);
local_port = strtonum("0x" local[2]);
remote_ip = convert_ip(remote[1]);
remote_port = strtonum("0x" remote[2]);
# 转换状态
state_str = convert_state(state);
printf "%-6s %-21s %-21s %-12s %-6s\n", $1,
local_ip ":" local_port,
remote_ip ":" remote_port,
state_str,
uid
}
function convert_ip(hex) {
# 小端序转换
return strtonum("0x" substr(hex,7,2)) "." \
strtonum("0x" substr(hex,5,2)) "." \
strtonum("0x" substr(hex,3,2)) "." \
strtonum("0x" substr(hex,1,2))
}
function convert_state(hex) {
states["00"] = "ESTABLISHED";
states["01"] = "SYN_SENT";
states["02"] = "SYN_RECV";
states["03"] = "FIN_WAIT1";
states["04"] = "FIN_WAIT2";
states["05"] = "TIME_WAIT";
states["06"] = "CLOSE";
states["07"] = "CLOSE_WAIT";
states["08"] = "LAST_ACK";
states["09"] = "LISTEN";
states["0A"] = "CLOSING";
return states[hex];
}' /proc/net/tcp
3.2 高级分析示例
3.2.1 统计各状态连接数
bash复制awk 'NR > 1 {
state[$4]++
}
END {
print "TCP连接状态统计:"
for (s in state) {
printf "状态 %2s: %4d 个连接\n", s, state[s]
}
}' /proc/net/tcp
3.2.2 检测异常连接
这段脚本可以检测可能的安全威胁:
- 大量SYN_RECV状态(可能是SYN洪水攻击)
- 非常用端口的外部连接
- root用户建立的异常连接
bash复制awk 'BEGIN {
print "开始异常连接检测..."
}
NR > 1 {
# 提取远程IP和端口
split($3, remote, ":");
remote_ip = convert_ip(remote[1]);
remote_port = strtonum("0x" remote[2]);
# 检测SYN洪水迹象
if ($4 == "02") {
syn_recv[remote_ip]++
}
# 检测非常用端口(>10000)的外部连接
if (remote_port > 10000 && $4 == "01") {
print "警告: 非常用端口连接 ->", remote_ip ":" remote_port
}
# 检测root用户的异常连接
if ($10 == "0" && $4 == "01") {
print "警告: root用户建立的连接 ->", remote_ip ":" remote_port
}
}
END {
print "\nSYN_RECV统计(可能的SYN洪水):"
for (ip in syn_recv) {
if (syn_recv[ip] > 10) {
printf "%15s: %d 次\n", ip, syn_recv[ip]
}
}
}
function convert_ip(hex) {
return strtonum("0x" substr(hex,7,2)) "." \
strtonum("0x" substr(hex,5,2)) "." \
strtonum("0x" substr(hex,3,2)) "." \
strtonum("0x" substr(hex,1,2))
}' /proc/net/tcp
4. 生产环境监控方案
4.1 性能优化技巧
直接解析/proc/net/tcp虽然强大,但在高负载服务器上可能影响性能。我在生产环境中总结了这些优化经验:
- 采样频率控制:不要超过每秒1次,繁忙服务器建议5-10秒间隔
- 增量分析:记录上次检查的SL序号,只处理新增连接
- 白名单过滤:忽略已知的安全连接,减少处理量
- 使用ss替代:对实时性要求不高的场景,
ss -t -a性能更好
4.2 完整监控脚本示例
这是一个经过实战检验的TCP连接监控脚本,包含:
- 连接状态实时监控
- 异常检测
- 性能数据记录
- 邮件报警功能
bash复制#!/bin/bash
# 配置参数
INTERVAL=10
LOG_FILE="/var/log/tcp_monitor.log"
ALERT_EMAIL="admin@example.com"
# 状态阈值
SYN_RECV_THRESHOLD=20
ESTABLISHED_THRESHOLD=500
monitor_tcp() {
local timestamp=$(date "+%Y-%m-%d %H:%M:%S")
local stats=$(awk -v threshold=$SYN_RECV_THRESHOLD '
BEGIN {
total=0; syn_recv=0; established=0;
print "=== 开始分析 ===" > "/dev/stderr"
}
NR > 1 {
total++
if ($4 == "02") syn_recv++
if ($4 == "01") established++
# 异常连接检测
if ($4 == "02" && syn_recv_ip[$3]++ > threshold) {
print "ALERT: SYN_RECV from " $3 " count: " syn_recv_ip[$3] > "/dev/stderr"
}
}
END {
printf "total=%d\nsyn_recv=%d\nestablished=%d\n", total, syn_recv, established
}' /proc/net/tcp)
# 解析统计结果
eval $stats
# 记录日志
echo "$timestamp - 总连接: $total, SYN_RECV: $syn_recv, ESTABLISHED: $established" >> $LOG_FILE
# 触发报警
if [ $syn_recv -gt $SYN_RECV_THRESHOLD ]; then
echo "$timestamp - 警告: SYN_RECV连接数异常 ($syn_recv)" | \
mail -s "TCP监控报警" $ALERT_EMAIL
fi
}
# 主循环
while true; do
monitor_tcp
sleep $INTERVAL
done
4.3 与其它工具对比
| 工具/方法 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| /proc/net/tcp | 最底层数据,信息全面,适合自动化处理 | 格式复杂,需要解析,性能开销较大 | 深度分析,自定义监控 |
| netstat | 使用简单,信息易读 | 已过时,性能较差 | 临时检查,简单统计 |
| ss | 性能好,信息丰富 | 输出格式不固定,解析稍复杂 | 日常监控,快速排查 |
| lsof | 可以关联到具体进程 | 性能较差,信息有限 | 定位进程关联的连接 |
| 商业监控工具 | 功能全面,可视化好 | 成本高,可能有性能开销 | 企业级监控 |
5. 疑难问题排查案例
5.1 案例一:TIME_WAIT堆积
某次线上服务升级后,服务器出现大量TIME_WAIT状态连接,导致新连接无法建立。通过分析/proc/net/tcp发现:
- 90%的TIME_WAIT连接指向同一个服务端口
- 连接生命周期极短(<1秒)
- 客户端使用短连接频繁调用
解决方案:
- 调整内核参数:
net.ipv4.tcp_tw_reuse=1和net.ipv4.tcp_tw_recycle=1 - 修改客户端为长连接模式
- 服务端设置合适的
SO_LINGER选项
注意:tcp_tw_recycle在NAT环境下可能导致问题,Linux 4.12+已移除该选项
5.2 案例二:SYN_RECV洪水攻击
监控脚本突然报警SYN_RECV连接激增,通过分析发现:
- 来自多个IP的SYN请求,但从不完成握手
- 每个IP的连接数有规律增长
- 目标端口为Web服务端口
应对措施:
- 启用syn cookies:
sysctl -w net.ipv4.tcp_syncookies=1 - 配置iptables限速:
bash复制iptables -A INPUT -p tcp --syn -m limit --limit 1/s -j ACCEPT iptables -A INPUT -p tcp --syn -j DROP - 联系云服务商启用DDoS防护
5.3 案例三:连接泄漏
一个Java应用运行一段时间后响应变慢,/proc/net/tcp显示:
- ESTABLISHED连接持续增长
- 很多连接空闲(tx_queue=rx_queue=0)
- 相同四元组(源/目标IP+端口)重复出现
根本原因:应用没有正确关闭连接,导致文件描述符泄漏。修复方法:
- 使用try-with-resources确保连接关闭
- 增加连接池空闲超时设置
- 添加应用层连接监控
6. 进阶技巧与工具集成
6.1 内核参数调优
基于TCP连接监控数据,可以针对性调整这些内核参数:
bash复制# 增大最大连接数
sysctl -w net.core.somaxconn=32768
sysctl -w net.ipv4.tcp_max_syn_backlog=8192
# 加快TIME_WAIT回收
sysctl -w net.ipv4.tcp_fin_timeout=30
sysctl -w net.ipv4.tcp_max_tw_buckets=200000
# 提高TCP窗口大小
sysctl -w net.ipv4.tcp_window_scaling=1
sysctl -w net.ipv4.tcp_rmem="4096 87380 6291456"
sysctl -w net.ipv4.tcp_wmem="4096 16384 4194304"
6.2 与Prometheus集成
将TCP连接数据导出为Prometheus指标:
bash复制#!/bin/bash
# 定义指标文件
METRICS_FILE="/var/lib/node_exporter/tcp_metrics.prom"
generate_metrics() {
awk '
BEGIN {
print "# HELP node_tcp_connections Total number of TCP connections by state"
print "# TYPE node_tcp_connections gauge"
}
NR > 1 {
states[$4]++
}
END {
for (s in states) {
print "node_tcp_connections{state=\"" convert_state(s) "\"} " states[s]
}
}
function convert_state(hex) {
states["00"] = "established";
states["01"] = "syn_sent";
states["02"] = "syn_recv";
states["03"] = "fin_wait1";
states["04"] = "fin_wait2";
states["05"] = "time_wait";
states["06"] = "close";
states["07"] = "close_wait";
states["08"] = "last_ack";
states["09"] = "listen";
states["0A"] = "closing";
return states[hex];
}' /proc/net/tcp > $METRICS_FILE
}
# 定期更新指标
while true; do
generate_metrics
sleep 15
done
6.3 可视化方案
使用Grafana展示TCP连接状态趋势:
-
仪表板配置:
- 按状态统计的连接数时序图
- 异常连接的地理分布
- 队列堆积告警
-
关键查询示例:
promql复制# 检测SYN洪水 rate(node_tcp_connections{state="syn_recv"}[1m]) > 10 # 连接泄漏检测 increase(node_tcp_connections{state="established"}[1h]) > 1000 -
告警规则:
yaml复制- alert: TcpSynFlood expr: rate(node_tcp_connections{state="syn_recv"}[5m]) > 20 for: 10m labels: severity: critical annotations: summary: "SYN flood detected on {{ $labels.instance }}"
在实际运维中,我发现结合TCP连接监控与系统负载、应用日志等数据,能更准确判断问题根源。比如当TIME_WAIT连接激增时,需要同时检查:
- 应用日志中的连接错误
- 系统负载是否正常
- 网络流量是否异常
- 文件描述符使用量
这种多维度的关联分析往往能快速定位到真正的问题源头。
