服务响应变慢,浏览器转圈,运维第一反应是「不卡吧」。查完 CPU、内存、磁盘都没事,最后往往落在网络上:连接数爆炸、带宽打满、TIME_WAIT 堆积、重传率高。网络问题隐蔽、跨主机,排查难度比单机资源高。这一篇用 ss、netstat、iftop、sar -n 四把工具,从连接状态、带宽占用、历史趋势三个角度把网络看清。
一、网络性能的四个维度
排查网络,先明确你要回答哪个问题:
| 维度 | 问题 | 工具 |
|---|---|---|
| 连接 | 有多少连接?什么状态? | ss / netstat |
| 带宽 | 谁在占流量?跑满了吗? | iftop / nload |
| 质量 | 有丢包/重传/错误吗? | sar -n / netstat -s |
| 历史 | 过去某个时间点怎么样? | sar -n |
「连接多」和「带宽满」是两回事,很多人混为一谈。连接数多可能是短连接风暴(TIME_WAIT),带宽满则是有大流量传输。用不同的工具看。
二、ss:现代版 netstat(首选)
ss = socket statistics,netstat 的替代品。它直接读内核的 netlink 接口,比 netstat 遍历 /proc 快得多,几万连接时差距非常明显。新系统优先用 ss。
语法格式
ss [选项] [过滤]
常用参数
| 参数 | 说明 |
|---|---|
-t |
TCP |
-u |
UDP |
-n |
不解析端口名/主机名,显示数字(快) |
-l |
只显示 LISTEN 状态的套接字 |
-p |
显示进程(需 root 看全部) |
-a |
显示所有 |
-s |
打印汇总统计 |
-o |
显示定时器信息(含重传) |
-i |
显示 TCP 内部信息(含 cwnd、rtt) |
示例与预期输出
看所有监听端口:
sudo ss -tlnp
预期输出:
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
LISTEN 0 128 0.0.0.0:22 0.0.0.0:* users:(("sshd",pid=980))
LISTEN 0 511 0.0.0.0:80 0.0.0.0:* users:(("nginx",pid=1234))
LISTEN 0 128 127.0.0.1:3306 0.0.0.0:* users:(("mysqld",pid=2345))
关键列:
Recv-Q/Send-Q:对 LISTEN 状态,这是全连接队列(accept queue)的当前长度 / 上限 。Recv-Q持续接近Send-Q,说明应用来不及 accept,连接会溢出。Local Address:Port:0.0.0.0表示监听所有网卡,127.0.0.1表示只监听本地。
统计各状态连接数(排查 TIME_WAIT 必备):
ss -tan | awk 'NR>1 {print $1}' | sort | uniq -c | sort -rn
预期输出:
15234 ESTAB
3421 TIME-WAIT
128 LISTEN
12 CLOSE-WAIT
看某端口的连接来自哪些 IP:
ss -tan state established '( sport = :80 )' | awk 'NR>1{print $5}' | cut -d: -f1 | sort | uniq -c | sort -rn | head
看 TCP 内部指标(诊断慢连接):
ss -tin state established '( dport = :443 )'
输出里会带 rtt、cwnd、retrans 等,重传次数高说明网络质量差。
⚠️ Recv-Q 溢出是「连接被拒」的常见根因
如果服务负载高时客户端报 connection refused 或超时,但 ss 看监听在、进程也在,很可能是全连接队列满了 。Recv-Q 顶到 Send-Q 就是证据。解决方向:调大 net.core.somaxconn 和 nginx 的 backlog,或优化应用 accept 速度。
三、netstat:老将,仍要会读
netstat 属于 net-tools 包,很多精简系统默认不装。虽然被 ss 取代,但面试和排障仍常遇到,得会读。
语法格式
netstat [选项]
常用参数
| 参数 | 说明 |
|---|---|
-tlnp |
TCP 监听 + 数字 + 进程 |
-an |
所有连接(数字) |
-s |
协议统计(重传、错误) |
-r |
路由表 |
-i |
网卡统计 |
示例与预期输出
看重传、错误等协议统计:
netstat -s | grep -iE "retrans|error|drop|overflow"
1203 segments retransmitted
56 packets received with errors
89 times the listen queue of a socket overflowed
listen queue overflowed 直接对应上面说的全连接队列溢出,是个非常有用的信号。
注意:netstat 在连接数上万时会很慢(遍历 /proc/net/tcp),此时务必改用 ss。
四、iftop:实时看谁在吃带宽
ss/netstat 看连接,iftop 看流量排名------类似 top 之于进程。
安装
sudo apt install iftop -y # Ubuntu
sudo dnf install iftop -y # Rocky/CentOS
使用
sudo iftop -i eth0 -nNP
| 参数 | 说明 |
|---|---|
-i 网卡 |
指定网卡 |
-n |
不做 DNS 解析 |
-N |
显示端口号 |
-P |
显示端口列 |
-B |
以字节显示 |
预期输出:
12.5Mb 25.0Mb 37.5Mb
└───────────────┴───────────────┴───────────────
10.0.0.5:443 <=> 203.0.113.7:54321 5.20Mb 3.10Mb 2.80Mb
10.0.0.5:3306 <=> 10.0.0.9:41234 1.10Mb 980kb 870kb
左列本机地址,中间箭头方向(=> 发送,<= 接收,<=> 双向),右侧三列是 2s/10s/40s 平均速率。一眼看出是谁在传大流量。
iftop 还有两个常用键:p 切换显示端口,h 帮助,q 退出。轻量替代品是 nload(看总量)和 iptraf-ng。
五、sar -n:历史网络数据
ss/iftop 都是看「现在」。想知道「凌晨 3 点网络为什么抖动」,得靠 sar 的历史记录(它默认每 10 分钟采样一次并保留一个月)。
# 看今天的网络统计
sar -n DEV 1 5
# 看指定日期的网络
sar -n DEV -f /var/log/sysstat/sa15
预期输出:
10:35:01 IFACE rxpck/s txpck/s rxkB/s txkB/s rxcmp/s txcmp/s rxmcst/s
10:35:02 eth0 2150.00 1980.00 5800.00 6200.00 0.00 0.00 0.00
常用 -n 子选项:
| 子选项 | 含义 |
|---|---|
DEV |
网卡流量 |
EDEV |
网卡错误/丢包 |
TCP |
TCP 连接统计 |
SOCK |
socket 使用情况(看 tw 等) |
排查历史网络问题,sar -n EDEV 看丢包错误,sar -n TCP 看重传和连接建立,配合时间点定位。
注意:
sar依赖sysstat服务持续采集。默认可能没开,需编辑/etc/default/sysstat把ENABLED="false"改成true并启动sysstat服务。
六、读懂 TCP 状态:TIME_WAIT 与 CLOSE_WAIT
这两个状态是网络排障里的常客,也是被误解最多的。
TIME_WAIT(主动关闭方进入)
四次挥手时,主动关闭连接的一方 最后进入 TIME_WAIT,持续 2MSL(通常 60 秒)才真正释放。
为什么要等? 两个原因:
- 保证最后一个 ACK 能到达对端,万一丢了可以对端重发 FIN,我方还能响应。
- 让本次连接的老数据包在网络中彻底消失,避免影响同端口的新连接。
TIME_WAIT 大量出现通常是正常的,尤其短连接服务(HTTP 短连接、爬虫)。它是「优雅关闭」的必要代价。
什么时候要处理?
- 端口被耗尽(客户端主动连大量目标,本地端口用光):开
net.ipv4.tcp_tw_reuse(只对客户端有效)。 - 服务端大量 TIME_WAIT:更该优化成长连接(keep-alive),从根上减少连接数。
CLOSE_WAIT(被动关闭方没关)
如果是被动关闭方 收到 FIN 后一直停在 CLOSE_WAIT,那基本是应用 bug :应用没调用 close()。CLOSE_WAIT 堆积会耗尽文件描述符。
关键区别:
| 状态 | 谁进入 | 是否正常 | 处理 |
|---|---|---|---|
| TIME_WAIT | 主动关闭方 | 正常现象 | 优化为长连接 |
| CLOSE_WAIT | 被动关闭方 | 异常 | 查应用代码为何不 close |
看到 CLOSE_WAIT 多,别去调内核参数,去改代码。
⚠️ 常见错误
- TIME_WAIT 多就慌 。它是正常状态,先看数量级和场景,别盲目调
tcp_tw_recycle(该参数在 4.12 内核已被移除,且 NAT 环境下有害)。 - CLOSE_WAIT 当 TIME_WAIT 处理。调内核参数没用,根因在应用没关连接。
- 改
tcp_tw_reuse却没生效 。它只对主动发起连接的一方有效,服务端被动连接不适用。 - 用 netstat 在几万连接时排查 。卡到怀疑人生,果断换
ss。 - 忽略全连接队列溢出 。
netstat -s的listen queue overflowed和ss的Recv-Q满,是「连接被拒」的隐形杀手。 - 只在本机看 。网络问题跨主机,本机
ss正常不代表网络正常,要两端都看,必要时抓包(第 74 篇)。
七、原理探究:TCP 连接队列与三次握手
理解 ss 输出里的 Recv-Q/Send-Q,得先懂内核怎么处理新连接。
三个队列
服务端 listen() 后,内核维护两个队列:
- 半连接队列(SYN queue) :收到 SYN、回了 SYN-ACK,等待对方 ACK 的连接。长度上限
net.ipv4.tcp_max_syn_backlog。 - 全连接队列(accept queue) :三次握手完成、等待应用
accept()的连接。长度上限是min(somaxconn, backlog),其中backlog由listen()传入,somaxconn=net.core.somaxconn。
握手与队列流转
客户端 服务端内核 应用
|--- SYN ----------------->| |
| | 入【半连接队列】 |
|<-- SYN+ACK ---------------| |
|--- ACK ------------------>| |
| | 移入【全连接队列】 |
| |---------------------->| accept()
ss -lnt 里 LISTEN 状态的 Recv-Q 就是全连接队列的当前长度 ,Send-Q 是它的上限。当 Recv-Q 顶到 Send-Q,新来的握手完成连接会被丢弃 (或按 tcp_abort_on_overflow 决策直接 RST),客户端表现为超时或拒绝。
调优参数
# 全连接队列上限(应用 listen backlog 也受它限制)
sysctl net.core.somaxconn
# 半连接队列上限
sysctl net.ipv4.tcp_max_syn_backlog
# SYN 队列满时是否直接 RST(默认 0,丢弃让它重试)
sysctl net.ipv4.tcp_abort_on_overflow
配合应用侧调大 listen() 的 backlog(如 nginx 的 listen ... backlog=2048;),才能真正扩容。
为什么会有 SYN Flood 攻击
攻击者只发 SYN 不回最后的 ACK,半连接队列被占满,正常连接进不来。防御手段:SYN cookies(net.ipv4.tcp_syncookies=1)、调大 tcp_max_syn_backlog、缩短 tcp_synack_retries。
八、网络排查速查表
# 1. 监听端口与队列
sudo ss -tlnp
# 2. 各状态连接数统计
ss -tan | awk 'NR>1{print $1}' | sort | uniq -c | sort -rn
# 3. 某端口连接来源 Top
sudo ss -tn state established '( sport = :80 )' | awk 'NR>1{print $5}' | cut -d: -f1 | sort | uniq -c | sort -rn | head
# 4. 实时流量排名
sudo iftop -i eth0 -nNP
# 5. 协议错误统计
netstat -s | grep -iE "retrans|overflow|error"
# 6. 历史网络数据
sar -n DEV 1 5
sar -n EDEV 1 5
一句话记住网络排查的主线:连接看 ss、流量看 iftop、质量看 sar/netstat -s、历史看 sar。而绝大多数「网络慢」背后,其实是 TIME_WAIT 堆积、队列溢出或应用没关连接------先分清是谁的问题,再谈优化。