ss 命令详解与参数实测(Ubuntu 22.04)
ss 逐参数实测详解:默认输出、-t/-u/-l/-n/-p/-a 基础过滤、-tuln 与 netstat 对照、-s 统计摘要、-m 套接字内存 skmem 解读、-o 计时器、-i 观测 cwnd 与 rtt、-e 查看 ino 与 uid、-4/-6 地址族、-x UNIX 域套接字、state 状态过滤与 dst/src/dport 表达式,含 -K 强制关连的权限受限实测记录,附 ss 与 netstat 映射表,全部回显在 Ubuntu 22.04(iproute2 5.15.0)实测通过。
ss 命令详解与参数实测(Ubuntu 22.04)
目标:全面掌握 ss 的每一个常用参数——默认输出、
-t/-u/-l/-n/-p/-a基础过滤、-tuln排查标配、-s统计摘要、-m套接字内存、-o计时器、-iTCP 内部信息(cwnd/rtt)、-e详细信息(ino/uid)、-4/-6地址族、-dDCCP、-xUNIX 域套接字,以及 state 状态过滤、dst/src/dport 地址过滤与-K强制关连的实测行为,附 ss 与 netstat 的对照与命令映射,全部命令与回显已在 Ubuntu 22.04(iproute2 5.15.0)实测通过。
一、环境信息
- 操作系统:Ubuntu 22.04.5 LTS(x86_64)
- ss 版本:iproute2 5.15.0(
ss -V实测),由iproute2软件包提供,Ubuntu 22.04 默认预装 - ss 通过内核 netlink 套接字(NETLINK_SOCK_DIAG)取数,不走 /proc 伪文件,这是它与 netstat 的本质区别(见 3.18 节)
- 测试数据目录:
/root/demo/ss/(存放模拟连接用的 Python 辅助脚本) - 演示端口:18000(演示用 HTTP 服务)、7778(echo 收发对)、7779(
-K实验)、8080/18081/9996(地址族演示)、9998(UDP)
二、命令概览
2.1 语法与测试数据
ss(socket statistics)用于查看内核套接字统计,支持按状态、地址、端口在内核侧过滤,基本语法是:
ss [选项] [状态过滤] [地址过滤表达式]
本文延续 netstat 篇的实测方法,在 /root/demo/ss/ 准备辅助脚本,每个演示块自带数据准备与清理,可直接复制复现:
# /root/demo/ss/hold.py —— 建立 TCP 连接并保持指定秒数(制造 ESTABLISHED)
import socket, sys, time
port = int(sys.argv[1]); secs = int(sys.argv[2])
s = socket.create_connection(("127.0.0.1", port))
time.sleep(secs)
# /root/demo/ss/echo_server.py —— accept 后回显一次再挂住(连接有流量,供 -i/-m/-o 观测)
import socket, sys, time
port = int(sys.argv[1]); secs = int(sys.argv[2])
srv = socket.socket(); srv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
srv.bind(("127.0.0.1", port)); srv.listen(1)
c, _ = srv.accept()
data = c.recv(65536)
c.sendall(b"ACK " + data[:20])
time.sleep(secs)
# /root/demo/ss/echo_client.py —— 客户端开启 keepalive、发送并接收一次后挂住
import socket, sys, time
port = int(sys.argv[1]); secs = int(sys.argv[2])
s = socket.create_connection(("127.0.0.1", port))
s.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
s.sendall(b"hello-ss-demo")
time.sleep(0.5)
s.recv(1024)
time.sleep(secs)
# /root/demo/ss/unix_server.py / unix_client.py —— UNIX 域套接字演示对
import socket, sys, time
path = sys.argv[1]; secs = int(sys.argv[2])
srv = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
srv.bind(path); srv.listen(1)
c, _ = srv.accept()
time.sleep(secs)
# unix_client.py
import socket, sys, time
path = sys.argv[1]; secs = int(sys.argv[2])
s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
s.connect(path)
time.sleep(secs)
演示服务仍用 python3 -m http.server 18000 --bind 127.0.0.1 提供 LISTEN 态,hold.py 挂 ESTABLISHED 态,curl 短请求制造 TIME_WAIT 态。
2.2 --help 全量输出
ss --help 逐行照抄如下(注意末尾的 STATE-FILTER 语法正是后文 3.15 状态过滤的官方说明):
Usage: ss [ OPTIONS ]
ss [ OPTIONS ] [ FILTER ]
-h, --help this message
-V, --version output version information
-n, --numeric don't resolve service names
-r, --resolve resolve host names
-a, --all display all sockets
-l, --listening display listening sockets
-o, --options show timer information
-e, --extended show detailed socket information
-m, --memory show socket memory usage
-p, --processes show process using socket
-i, --info show internal TCP information
--tipcinfo show internal tipc socket information
-s, --summary show socket usage summary
--tos show tos and priority information
--cgroup show cgroup information
-b, --bpf show bpf filter socket information
-E, --events continually display sockets as they are destroyed
-Z, --context display process SELinux security contexts
-z, --contexts display process and socket SELinux security contexts
-N, --net switch to the specified network namespace name
-4, --ipv4 display only IP version 4 sockets
-6, --ipv6 display only IP version 6 sockets
-0, --packet display PACKET sockets
-t, --tcp display only TCP sockets
-M, --mptcp display only MPTCP sockets
-S, --sctp display only SCTP sockets
-u, --udp display only UDP sockets
-d, --dccp display only DCCP sockets
-w, --raw display only RAW sockets
-x, --unix display only Unix domain sockets
--tipc display only TIPC sockets
--vsock display only vsock sockets
-f, --family=FAMILY display sockets of type FAMILY
FAMILY := {inet|inet6|link|unix|netlink|vsock|tipc|xdp|help}
-K, --kill forcibly close sockets, display what was closed
-H, --no-header Suppress header line
-O, --oneline socket's data printed on a single line
--inet-sockopt show various inet socket options
-A, --query=QUERY, --socket=QUERY
QUERY := {all|inet|tcp|mptcp|udp|raw|unix|unix_dgram|unix_stream|unix_seqpacket|packet|netlink|vsock_stream|vsock_dgram|tipc}[,QUERY]
-D, --diag=FILE Dump raw information about TCP sockets to FILE
-F, --filter=FILE read filter information from FILE
FILTER := [ state STATE-FILTER ] [ EXPRESSION ]
STATE-FILTER := {all|connected|synchronized|bucket|big|TCP-STATES}
TCP-STATES := {established|syn-sent|syn-recv|fin-wait-{1,2}|time-wait|closed|close-wait|last-ack|listening|closing}
connected := {established|syn-sent|syn-recv|fin-wait-{1,2}|time-wait|close-wait|last-ack|closing}
synchronized := {established|syn-recv|fin-wait-{1,2}|time-wait|close-wait|last-ack|closing}
bucket := {syn-recv|time-wait}
big := {established|syn-sent|fin-wait-{1,2}|closed|close-wait|last-ack|listening|closing}
2.3 版本信息
ss -V
ss utility, iproute2-5.15.0
三、参数逐项实测
3.1 默认输出(已连接 TCP)
不带参数时 ss 只显示已连接的 inet 套接字。先起服务并挂一条保持连接:
python3 -m http.server 18000 --bind 127.0.0.1 >/dev/null 2>&1 &
python3 /root/demo/ss/hold.py 18000 60 >/dev/null 2>&1 &
ss | head -12
Netid State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
tcp ESTAB 0 0 127.0.0.1:18000 127.0.0.1:38828
tcp ESTAB 0 0 127.0.0.1:38828 127.0.0.1:18000
ss 的列结构与 netstat 不同,逐列说明:
| 列名 | 含义 |
|---|---|
| Netid | 套接字类型:tcp/udp/u_str(UNIX 流)/u_seq/nl(netlink)/p_raw 等,仅 -a 或含非 inet 视图时出现 |
| State | TCP 状态,ss 用缩写(ESTAB/LISTEN/TIME-WAIT/CLOSE-WAIT),比 netstat 的全称短 |
| Recv-Q / Send-Q | 与 netstat 同义:ESTAB 态为字节队列,LISTEN 态 Recv-Q 为待 accept 连接数、Send-Q 为 backlog 上限 |
| Local Address:Port / Peer Address:Port | 本端与对端地址:端口 |
| Process | -p 时显示 users:(("进程名",pid=N,fd=M)) |
与 netstat 默认视图的两点差异(对照 netstat 篇 3.1 实测):ss 默认不显示 UNIX 域已连接套接字;ss 默认不做主机名反解(直接显示 127.0.0.1,netstat 显示 localhost),所以 ss 默认就很快。
3.2 -t / -u:协议过滤
-t 只看 TCP,输出格式与默认一致但去掉 Netid 列;-u 只看 UDP:
ss -t
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
ESTAB 0 0 127.0.0.1:18000 127.0.0.1:38828
ESTAB 0 0 127.0.0.1:38828 127.0.0.1:18000
ss -u; echo rc=$?
Recv-Q Send-Q Local Address:Port Peer Address:PortProcess
rc=0
ss -u 空输出 + rc=0:默认视图只含「已连接」UDP,本机没有;要看 UDP 监听端口需加 -a 或 -l(见 3.4 中 udp UNCONN 行)。
3.3 -n:不解析服务名
ss 默认会把端口号翻译成服务名(查 /etc/services),-n 关闭端口名解析。对比同一监听端口在两种模式下的显示:
ss -tl
State Recv-Q Send-Q Local Address:Port Peer Address:PortProcess
LISTEN 0 128 0.0.0.0:ssh 0.0.0.0:*
LISTEN 0 128 0.0.0.0:2222 0.0.0.0:*
LISTEN 0 5 0.0.0.0:8001 0.0.0.0:*
LISTEN 0 5 0.0.0.0:8000 0.0.0.0:*
LISTEN 0 5 0.0.0.0:8443 0.0.0.0:*
LISTEN 0 5 127.0.0.1:18000 0.0.0.0:*
LISTEN 0 128 [::]:ssh [::]:*
LISTEN 0 128 [::]:2222 [::]:*
ss -tln | head -4
State Recv-Q Send-Q Local Address:Port Peer Address:PortProcess
LISTEN 0 128 0.0.0.0:22 0.0.0.0:*
LISTEN 0 128 0.0.0.0:2222 0.0.0.0:*
LISTEN 0 5 0.0.0.0:8001 0.0.0.0:*
LISTEN 0 5 0.0.0.0:8000 0.0.0.0:*
- 22 号端口默认显示
ssh、加-n后显示22;8000 在 Ubuntu 22.04 的/etc/services中无登记条目(netstat 篇 3.5 已实测),所以两种模式都显示数字。 - 注意
-n只关端口名解析:ss 对 IP 地址默认本来就显示数字,除非用-r主动反解主机名——这正好与 netstat 相反(netstat 默认反解,-n全关)。
3.4 -l:监听套接字 与 -tuln 组合
-l 只看 LISTEN;-tuln = TCP + UDP + 监听 + 数字,是服务器排查标配。先挂一个 UDP 套接字再对比:
python3 -c "import socket,time; s=socket.socket(socket.AF_INET,socket.SOCK_DGRAM); s.bind((\"127.0.0.1\",9998)); time.sleep(30)" >/dev/null 2>&1 &
ss -l
Netid State Recv-Q Send-Q Local Address:Port Peer Address:PortProcess
nl UNCONN 0 0 rtnl:kernel *
nl UNCONN 960 0 rtnl:1819 *
nl UNCONN 4352 0 tcpdiag:ss/7263 *
nl UNCONN 960 0 tcpdiag:kernel *
nl UNCONN 0 0 xfrm:kernel *
nl UNCONN 0 0 audit:kernel *
nl UNCONN 0 0 fiblookup:kernel *
nl UNCONN 0 0 nft:kernel *
nl UNCONN 0 0 uevent:kernel *
nl UNCONN 0 0 genl:kernel *
udp UNCONN 0 0 127.0.0.1:9998 0.0.0.0:*
tcp LISTEN 0 128 0.0.0.0:ssh 0.0.0.0:*
tcp LISTEN 0 128 0.0.0.0:2222 0.0.0.0:*
tcp LISTEN 0 5 0.0.0.0:8001 0.0.0.0:*
tcp LISTEN 0 5 0.0.0.0:8000 0.0.0.0:*
tcp LISTEN 0 5 0.0.0.0:8443 0.0.0.0:*
tcp LISTEN 0 5 127.0.0.1:18000 0.0.0.0:*
tcp LISTEN 0 128 [::]:ssh [::]:*
tcp LISTEN 0 128 [::]:2222 [::]:*
ss -tuln
Netid State Recv-Q Send-Q Local Address:Port Peer Address:PortProcess
udp UNCONN 0 0 127.0.0.1:9998 0.0.0.0:*
tcp LISTEN 0 128 0.0.0.0:22 0.0.0.0:*
tcp LISTEN 0 128 0.0.0.0:2222 0.0.0.0:*
tcp LISTEN 0 5 0.0.0.0:8001 0.0.0.0:*
tcp LISTEN 0 5 0.0.0.0:8000 0.0.0.0:*
tcp LISTEN 0 5 0.0.0.0:8443 0.0.0.0:*
tcp LISTEN 0 5 127.0.0.1:18000 0.0.0.0:*
tcp LISTEN 0 128 [::]:22 [::]:*
tcp LISTEN 0 128 [::]:2222 [::]:*
要点:
- 不加
-t/-u限定时,ss -l会带出一堆nl(netlink)行——那是 ss 自己取数用的内核通道套接字,属正常现象;实战更常用ss -tuln直接限定 inet 族。 - UDP 套接字状态显示
UNCONN(未连接);TCP 监听显示LISTEN,Send-Q=128/5 就是各进程listen()的 backlog。 - 与 netstat 篇
-tuln实测输出逐行对齐:监听集合完全一致,仅表头措辞与状态缩写不同。
💡 说明:
-tuln是 ss 最高频的组合——先确认「这台服务器开了哪些端口」,Send-Q列在 LISTEN 态就是各进程listen()的 backlog 上限(实测 128/5 与进程设定一致),排查「连接队列溢出」先看这一列;要落到进程再补-p(见 3.5)。
3.5 -p:进程信息
-p 显示占用套接字的进程,格式为 users:(("进程名",pid=N,fd=M)):
ss -tlp
State Recv-Q Send-Q Local Address:Port Peer Address:PortProcess
LISTEN 0 128 0.0.0.0:ssh 0.0.0.0:* users:(("sshd",pid=2423,fd=3))
LISTEN 0 128 0.0.0.0:2222 0.0.0.0:* users:(("sshd",pid=2914,fd=3))
LISTEN 0 5 0.0.0.0:8001 0.0.0.0:* users:(("python3",pid=4159,fd=3))
LISTEN 0 5 0.0.0.0:8000 0.0.0.0:* users:(("python3",pid=7155,fd=3))
LISTEN 0 5 0.0.0.0:8443 0.0.0.0:* users:(("python3",pid=4160,fd=3))
LISTEN 0 5 127.0.0.1:18000 0.0.0.0:* users:(("python3",pid=7246,fd=3))
LISTEN 0 128 [::]:ssh [::]:* users:(("sshd",pid=2423,fd=4))
LISTEN 0 128 [::]:2222 [::]:* users:(("sshd",pid=2914,fd=4))
ss -tnp | grep 18000
ESTAB 0 0 127.0.0.1:18000 127.0.0.1:38828 users:(("python3",pid=7246,fd=4))
ESTAB 0 0 127.0.0.1:38828 127.0.0.1:18000 users:(("python3",pid=7248,fd=3))
- 与 netstat 的 PID/Program name 列相比,ss 多给了文件描述符号(fd=3 是监听 socket,fd=4 是 accept 出来的连接),定位代码里泄漏的 fd 时很有用。
- TIME_WAIT 等无属主套接字在
-p下 Process 列留空(内核中没有对应打开的 fd)。 - 非 root 运行时只能归属自己的进程,与 netstat
-p相同。
3.6 -a:全部套接字
-a 显示全部(监听 + 已连接 + 各种中间状态),TCP 视图用 -at:
python3 -m http.server 18000 --bind 127.0.0.1 >/dev/null 2>&1 &
python3 /root/demo/ss/hold.py 18000 60 >/dev/null 2>&1 &
ss -at
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
LISTEN 0 128 0.0.0.0:ssh 0.0.0.0:*
LISTEN 0 128 0.0.0.0:2222 0.0.0.0:*
LISTEN 0 5 0.0.0.0:8001 0.0.0.0:*
LISTEN 0 5 0.0.0.0:8000 0.0.0.0:*
LISTEN 0 5 0.0.0.0:8443 0.0.0.0:*
LISTEN 0 5 127.0.0.1:18000 0.0.0.0:*
TIME-WAIT 0 0 127.0.0.1:8000 127.0.0.1:50772
ESTAB 0 0 127.0.0.1:18000 127.0.0.1:38828
TIME-WAIT 0 0 127.0.0.1:8000 127.0.0.1:50792
TIME-WAIT 0 0 127.0.0.1:8000 127.0.0.1:46180
TIME-WAIT 0 0 127.0.0.1:8000 127.0.0.1:50780
TIME-WAIT 0 0 127.0.0.1:8000 127.0.0.1:46182
TIME-WAIT 0 0 127.0.0.1:8000 127.0.0.1:51114
TIME-WAIT 0 0 127.0.0.1:50068 127.0.0.1:8000
TIME-WAIT 0 0 127.0.0.1:8000 127.0.0.1:50788
TIME-WAIT 0 0 127.0.0.1:8000 127.0.0.1:50068
TIME-WAIT 0 0 127.0.0.1:8000 127.0.0.1:50790
TIME-WAIT 0 0 127.0.0.1:50798 127.0.0.1:8000
TIME-WAIT 0 0 127.0.0.1:8000 127.0.0.1:50754
TIME-WAIT 0 0 127.0.0.1:8000 127.0.0.1:51110
TIME-WAIT 0 0 127.0.0.1:18080 127.0.0.1:46468
TIME-WAIT 0 0 127.0.0.1:46182 127.0.0.1:8000
TIME-WAIT 0 0 127.0.0.1:18080 127.0.0.1:46482
TIME-WAIT 0 0 127.0.0.1:8000 127.0.0.1:50760
TIME-WAIT 0 0 127.0.0.1:8000 127.0.0.1:46176
ESTAB 0 0 127.0.0.1:38828 127.0.0.1:18000
LISTEN 0 128 [::]:ssh [::]:*
LISTEN 0 128 [::]:2222 [::]:*
输出节选(实测全表)。状态缩写对照 netstat:TIME-WAIT=TIME_WAIT、ESTAB=ESTABLISHED、CLOSE-WAIT=CLOSE_WAIT、UNCONN=无连接。大量 127.0.0.1:8000 的 TIME-WAIT 是本机其他服务的短连接残留,恰好演示了 TIME-WAIT 堆积的典型形态。
3.7 -s:统计摘要
-s 给出套接字总量摘要(先制造流量与状态再统计):
python3 -m http.server 18000 --bind 127.0.0.1 >/dev/null 2>&1 &
python3 -c "import socket,time; s=socket.socket(socket.AF_INET,socket.SOCK_DGRAM); s.bind((\"127.0.0.1\",9998)); time.sleep(20)" >/dev/null 2>&1 &
python3 /root/demo/ss/hold.py 18000 20 >/dev/null 2>&1 &
curl -s http://127.0.0.1:18000/ >/dev/null
ss -s
Total: 17
TCP: 70 (estab 4, closed 58, orphaned 0, timewait 24)
Transport Total IP IPv6
RAW 0 0 0
UDP 1 1 0
TCP 12 10 2
INET 12 10 2
FRAG 0 0 0
解读:
- 首行
Total: 17是当前活跃套接字总数;第二行TCP: 70 (estab 4, closed 58, ...)是 TCP 家族总量,括号内按状态细分:4 条已建立、58 条已关闭(内核对象尚未释放)、24 条 TIME-WAIT。 - Transport 表按协议族给出 IPv4/IPv6 分布:UDP 1 个(9998)、TCP 12 个(IPv4 10 + IPv6 2)。
ss -s是轻量摘要,适合塞进监控脚本;要看逐计数器的协议统计(packets/errs/drop),仍用netstat -s或nstat。
3.8 -m:套接字内存(skmem)
-m 在每条连接下追加 skmem 行,展示内核为该套接字分配的内存:
python3 /root/demo/ss/echo_server.py 7778 60 >/dev/null 2>&1 &
python3 /root/demo/ss/echo_client.py 7778 58 >/dev/null 2>&1 &
ss -tm
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
ESTAB 0 0 127.0.0.1:7778 127.0.0.1:60738
skmem:(r0,rb131072,t0,tb2626560,f0,w0,o0,bl0,d0)
ESTAB 0 0 127.0.0.1:60738 127.0.0.1:7778
skmem:(r0,rb131072,t0,tb2626560,f0,w0,o0,bl0,d0)
skmem 字段解读:
| 字段 | 含义 |
|---|---|
| r / rb | 接收队列已用字节 / 接收缓冲区上限 |
| t / tb | 发送队列已用字节 / 发送缓冲区上限 |
| f | forward allocation(预分配给接收的内存) |
| w | wmem allocation(发送侧已记账内存) |
| o | 选项等辅助内存 |
| bl | 协议私有 backlog(如 UDP 积压) |
| d | sock 之外挂的内存(如 UNIX 套接字的 skb) |
实测两条连接的 rb=131072(128KB 接收缓冲)、tb=2626560(约 2.5MB 发送缓冲上限,空闲时 t=0 未占用)。排障时若 r/rb 或 t/tb 长期逼近,说明缓冲区打满、应用读取或网络发送跟不上。
3.9 -o:计时器
-o 追加 timer 列,显示套接字上的内核定时器:
ss -to
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
ESTAB 0 0 127.0.0.1:7778 127.0.0.1:60738
ESTAB 0 0 127.0.0.1:60738 127.0.0.1:7778 timer:(keepalive,119min,0)
- 同一对连接,客户端(60738,echo_client.py 开启了 SO_KEEPALIVE)显示
timer:(keepalive,119min,0):keepalive 定时器、距下次探测约 119 分钟、已重传 0 次;服务端未开 keepalive,timer 列为空——正好构成对照。 - timer 取值类型:
on(等待 ACK/数据的重传定时器)、keepalive、timewait(TIME_WAIT 回收定时器)、off。排障口诀:大量连接 timer:on 且重传次数增长 = 对端无响应或网络异常;TIME_WAIT 连接会显示 timewait 定时器倒计时。
3.10 -i:TCP 内部信息(cwnd / rtt)
-i 展示每条 TCP 连接的内部参数,是观测拥塞窗口与往返时延的主要手段:
ss -ti
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
ESTAB 0 0 127.0.0.1:7778 127.0.0.1:60738
cubic wscale:7,7 rto:201 rtt:0.008/0.003 ato:40 mss:32768 pmtu:65535 rcvmss:536 advmss:65483 cwnd:10 bytes_sent:17 bytes_acked:17 bytes_received:13 segs_out:2 segs_in:4 data_segs_out:1 data_segs_in:1 send 328Gbps lastsnd:2021 lastrcv:2021 lastack:2021 pacing_rate 591Gbps delivery_rate 32.8Gbps delivered:2 app_limited rcv_space:65483 rcv_ssthresh:65483 minrtt:0.008
ESTAB 0 0 127.0.0.1:60738 127.0.0.1:7778
cubic wscale:7,7 rto:201 rtt:0.009/0.005 ato:40 mss:32768 pmtu:65535 rcvmss:536 advmss:65483 cwnd:10 bytes_sent:13 bytes_acked:14 bytes_received:17 segs_out:4 segs_in:3 data_segs_out:1 data_segs_in:1 send 291Gbps lastsnd:2025 lastrcv:2025 lastack:2025 pacing_rate 575Gbps delivery_rate 87.4Gbps delivered:2 app_limited rcv_space:65495 rcv_ssthresh:65495 minrtt:0.003
关键字段解读:
| 字段 | 实测值 | 含义 |
|---|---|---|
| rtt:0.008/0.003 | 毫秒 | 平滑 RTT / RTT 方差(回环连接极小);跨网连接通常 0.5~200ms |
| rto:201 | 毫秒 | 重传超时 = RTT + 4×方差 的推算值 |
| cwnd:10 | 段 | 拥塞窗口(初始 10),持续增长说明链路健康 |
| mss:32768 / pmtu:65535 | 字节 | 最大段长 / 路径 MTU(回环为 65535) |
| bytes_sent/acked/received | 字节 | 累计收发统计 |
| lastsnd / lastrcv / lastack | 毫秒 | 距上次发送/接收/ACK 的时长,判断连接是否僵死 |
| pacing_rate / delivery_rate | bps | 发送速率估计 |
| app_limited | 标记 | 应用层供数不足(非网络瓶颈)——本例 echo 对空闲,正好命中 |
排障经验:ss -ti 中 rtt 突增、cwnd 回落到个位数、retrans 计数增长三者同时出现,基本可判定链路质量恶化。
3.11 -e:详细信息(ino / uid)
-e 追加套接字元信息:inode 号(ino)、内核 sk 指针(sk)、cgroup 归属等。root 属主的套接字不显示 uid(uid 0 省略),用 nobody(65534)建立连接即可看到 uid 字段:
python3 -m http.server 18000 --bind 127.0.0.1 >/dev/null 2>&1 &
cp /root/demo/ss/hold.py /tmp/hold-uid.py && chmod 644 /tmp/hold-uid.py
setpriv --reuid=65534 --regid=65534 --clear-groups python3 /tmp/hold-uid.py 18000 40 >/dev/null 2>&1 &
ss -tne | grep 18000
ESTAB 0 0 127.0.0.1:47858 127.0.0.1:18000 uid:65534 ino:552386 sk:a001 cgroup:/ <->
ESTAB 0 0 127.0.0.1:18000 127.0.0.1:47858 ino:554725 sk:a004 cgroup:/ <->
uid:65534是 nobody 属主的连接,本端 root 服务的行不显示 uid——可用来快速区分「哪些连接是普通用户进程发起的」。ino是套接字 inode,与lsof//proc/<pid>/fd里的socket:[ino]对应,是把连接关联到进程的中间凭据;sk是内核 sock 结构地址。- 行尾
<->表示该套接字双向统计可用。
3.12 -4 / -6:地址族过滤
-4/-6 分别只显示 IPv4/IPv6 套接字。准备三个监听:IPv4 通配(8080)、仅回环 IPv4(18081)、仅 IPv6 回环([::1]:9996):
python3 -m http.server 8080 >/dev/null 2>&1 &
python3 -m http.server 18081 --bind 127.0.0.1 >/dev/null 2>&1 &
python3 -c "import socket,time; s=socket.socket(socket.AF_INET6); s.bind((\"::1\",9996)); s.listen(1); time.sleep(30)" >/dev/null 2>&1 &
ss -4 -tln
State Recv-Q Send-Q Local Address:Port Peer Address:PortProcess
LISTEN 0 128 0.0.0.0:22 0.0.0.0:*
LISTEN 0 128 0.0.0.0:2222 0.0.0.0:*
LISTEN 0 5 0.0.0.0:8001 0.0.0.0:*
LISTEN 0 5 0.0.0.0:8000 0.0.0.0:*
LISTEN 0 5 0.0.0.0:8080 0.0.0.0:*
LISTEN 0 5 0.0.0.0:8443 0.0.0.0:*
LISTEN 0 5 127.0.0.1:18081 0.0.0.0:*
ss -6 -tln
State Recv-Q Send-Q Local Address:Port Peer Address:PortProcess
LISTEN 0 128 [::]:22 [::]:*
LISTEN 0 128 [::]:2222 [::]:*
LISTEN 0 1 [::1]:9996 [::]:*
-4表里全是0.0.0.0/127.0.0.1的 IPv4 监听;-6表里是[::]/[::1]的 IPv6 监听,互不混杂。- sshd 在两张表各有一行(
0.0.0.0:22与[::]:22),是双栈服务的典型形态;排查「IPv6 有没有监听」时直接ss -6 -tln。
3.13 -d:DCCP 套接字
-d 只显示 DCCP(数据报拥塞控制协议)套接字。DCCP 极少部署,本机为空但命令正常:
ss -d; echo rc=$?
State Recv-Q Send-Q Local Address:Port Peer Address:PortProcess
rc=0
顺带从 --help 可知 ss 还支持 -w(RAW)、-S(SCTP)、-M(MPTCP)、-0(packet)、--tipc、--vsock 等族过滤,用法与 -d 一致。
3.14 -x:UNIX 域套接字
-x 显示 UNIX 域套接字,Netid 为 u_str(流式)。默认(不加 -a)只显示已连接的:
rm -f /tmp/demo.sock
python3 /root/demo/ss/unix_server.py /tmp/demo.sock 40 >/dev/null 2>&1 &
python3 /root/demo/ss/unix_client.py /tmp/demo.sock 40 >/dev/null 2>&1 &
ss -x
Netid State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
u_str ESTAB 0 0 * 556401 * 556400
u_str ESTAB 0 0 * 550427 * 0
u_str ESTAB 0 0 * 556400 * 556401
u_str ESTAB 0 0 /tmp/cm-scp.EUIcxcQwQrZqoqaW 551825 * 549842
u_str ESTAB 0 0 * 549842 * 551825
已连接行的 Local/Peer 显示的是套接字 inode 号(只有监听行显示路径),要看到进程归属与路径,加 -ap:
ss -xap | grep -E "t.sock|State"
Netid State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
u_str ESTAB 0 0 /tmp/t.sock 557634 * 552588 users:(("python3",pid=8442,fd=4))
u_str LISTEN 0 1 /tmp/t.sock 557633 * 0 users:(("python3",pid=8442,fd=3))
ss -xlp | grep -E "t.sock|State"
Netid State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
u_str LISTEN 0 1 /tmp/t.sock 557633 * 0 users:(("python3",pid=8442,fd=3))
-xap一次给出路径、状态与users:(("python3",pid=8442,fd=4))——UNIX 域排障(谁占着 /tmp/*.sock、fd 泄漏到哪)三板斧:ss -x看总量、ss -xa看监听、ss -xap归属进程。u_str ESTAB ... * 550427 * 0这种对端为* 0的行是尚未被 accept 或对端已关闭的匿名连接。
3.15 状态过滤:state established / time-wait
ss 支持把状态作为过滤表达式直接下推内核(--help 中 STATE-FILTER 语法的实测)。先制造 TIME_WAIT(curl 短连接刚结束)与 ESTABLISHED(hold.py 挂住):
python3 -m http.server 18000 --bind 127.0.0.1 >/dev/null 2>&1 &
python3 /root/demo/ss/hold.py 18000 25 >/dev/null 2>&1 &
curl -s http://127.0.0.1:18000/ >/dev/null
ss state time-wait
Netid Recv-Q Send-Q Local Address:Port Peer Address:Port Process
tcp 0 0 127.0.0.1:8000 127.0.0.1:59154
tcp 0 0 127.0.0.1:8000 127.0.0.1:59184
tcp 0 0 127.0.0.1:7778 127.0.0.1:60738
tcp 0 0 127.0.0.1:8000 127.0.0.1:33612
tcp 0 0 127.0.0.1:8000 127.0.0.1:59084
tcp 0 0 127.0.0.1:18000 127.0.0.1:46056
输出节选(实测共 26 行,其余均为 127.0.0.1:8000 相关残留)。其中 127.0.0.1:18000 ↔ 127.0.0.1:46056 就是本次 curl 主动关闭后留下的 TIME_WAIT。
ss state established
Netid Recv-Q Send-Q Local Address:Port Peer Address:Port Process
tcp 0 0 127.0.0.1:18000 127.0.0.1:46072
tcp 0 0 127.0.0.1:46072 127.0.0.1:18000
ss state listening | head -10
Netid Recv-Q Send-Q Local Address:Port Peer Address:PortProcess
tcp 0 128 0.0.0.0:ssh 0.0.0.0:*
tcp 0 128 0.0.0.0:2222 0.0.0.0:*
tcp 0 5 0.0.0.0:8001 0.0.0.0:*
tcp 0 5 0.0.0.0:8000 0.0.0.0:*
tcp 0 5 0.0.0.0:8443 0.0.0.0:*
tcp 0 5 127.0.0.1:18000 0.0.0.0:*
tcp 0 128 [::]:ssh [::]:*
tcp 0 128 [::]:2222 [::]:*
- 状态名用小写连字符:
established、time-wait、close-wait、listening、syn-sent、fin-wait-1等(对照 netstat 需要靠grep TIME_WAIT实现)。 - 不加
-t时 state 过滤会带出其他族里「同名状态」的套接字(如 UNIX 流的 ESTAB),实战建议写成ss -tn state established明确限定 TCP。 - STATE-FILTER 还提供聚合名:
connected(所有已连接类)、synchronized、bucket(syn-recv+time-wait)、big、all,例如ss state bucket一条命令集中查看半开与 TIME_WAIT 队列。
3.16 地址过滤:dst / src / dport
ss 的表达式过滤同样在内核侧执行,dst 匹配对端、src 匹配本端、dport/sport 匹配对端/本端端口:
python3 -m http.server 18000 --bind 127.0.0.1 >/dev/null 2>&1 &
python3 /root/demo/ss/hold.py 18000 20 >/dev/null 2>&1 &
ss -tn dst 127.0.0.1
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
ESTAB 0 0 127.0.0.1:37216 127.0.0.1:22
ESTAB 0 0 127.0.0.1:22 127.0.0.1:37216
ESTAB 0 0 127.0.0.1:18000 127.0.0.1:54206
ESTAB 0 0 127.0.0.1:54206 127.0.0.1:18000
ss -tn src 127.0.0.1
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
ESTAB 0 0 127.0.0.1:37216 127.0.0.1:22
ESTAB 0 0 127.0.0.1:22 127.0.0.1:37216
ESTAB 0 0 127.0.0.1:18000 127.0.0.1:54206
ESTAB 0 0 127.0.0.1:54206 127.0.0.1:18000
ss -tn dport = :18000
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
ESTAB 0 0 127.0.0.1:54206 127.0.0.1:18000
- 全部连接都在回环上,所以
dst与src命中同样 4 行;真实场景中它们分别用于「看谁连着我」(dst <客户端IP>)和「看我连着谁」(src <本机IP>)。 dport = :18000精确匹配对端端口为 18000 的连接;多个表达式并列即 AND(如ss -tn dst 127.0.0.1 dport = :18000,见 4.3 实测),也支持!=、>、<比较与or。
3.17 -K:强制关闭连接(谨慎使用)
-K(--kill)对命中过滤条件的套接字执行 SOCK_DESTROY,立即发 RST 断开。语法实测如下——先建一条挂起连接,再用端口过滤器定向关闭:
python3 /root/demo/ss/echo_server.py 7779 90 >/dev/null 2>&1 &
python3 /root/demo/ss/hold.py 7779 90 >/dev/null 2>&1 &
ss -tn | grep 7779
ESTAB 0 0 127.0.0.1:57956 127.0.0.1:7779
ESTAB 0 0 127.0.0.1:7779 127.0.0.1:57956
ss -K dport = :7779
SOCK_DESTROY answers: Operation not permitted
Netid State Recv-Q Send-Q Local Address:Port Peer Address:PortProcess
ss -tn | grep 7779; echo rc=$?
ESTAB 0 0 127.0.0.1:57956 127.0.0.1:7779
ESTAB 0 0 127.0.0.1:7779 127.0.0.1:57956
rc=0
grep CapEff /proc/self/status
CapEff: 00000000a80425fb
实测结论(谨慎项):
- 命令与过滤器语法正确(
ss -K dport = :7779),但本实测环境返回SOCK_DESTROY answers: Operation not permitted,连接未被关闭——SOCK_DESTROY 需要内核授予 CAP_NET_ADMIN 能力,本环境 CapEff 位图中未包含该位(0xa80425fb第 12 位为 0),即便以 root 运行也会被拒绝。 - 在标准服务器上 root 通常具备完整能力,等效命令将直接列出被关闭的套接字并断开连接。正因为它是唯一能在不杀进程的情况下定向拆掉一条 TCP 连接的内置工具,务必始终带过滤条件(如
dport = :端口),裸敲ss -K会断开当前系统全部命中套接字。 - 能力受限环境的替代:
kill <pid>(杀进程)、conntrack -D(删跟踪表项)或重启应用。
3.18 技术拓展:ss 与 netstat 的取数通道与映射
ss 隶属 iproute2 工具族,通过 netlink(NETLINK_SOCK_DIAG) 一次内核调用批量取回套接字信息,过滤器在内核侧生效;netstat 隶属 net-tools,逐个读取 /proc/net/tcp 等文本伪文件再在用户态解析过滤。连接数上万时,netstat 的 CPU 与延迟随连接数线性增长,ss 增长平缓,这是「ss 取数更快」的根源,也是 Ubuntu 22.04 默认不预装 net-tools、而 netstat 官方已停止功能维护的原因。两套命令常用组合映射(反向对照 netstat 篇第六节):
| 用途 | ss 写法 | netstat 等价写法 |
|---|---|---|
| 监听端口总览 | ss -tuln | netstat -tuln |
| 监听 + 进程 | ss -tlnp | netstat -tlnp |
| 全部 TCP(数字) | ss -tan | netstat -ant |
| 按状态筛选 | ss -tn state time-wait | netstat -ant | grep TIME_WAIT |
| 按地址/端口筛选 | ss -tn dst IP dport = :N | netstat -ant | grep IP |
| 统计摘要 | ss -s | netstat -s(逐计数器,配合 nstat) |
| 套接字内存 | ss -tm | 无对应(netstat 不支持) |
| TCP 内部参数 | ss -ti | 无对应(可间接看 ip tcp_metrics) |
| 计时器 | ss -to | netstat -o(显示有限) |
| 强制关连 | ss -K | 无对应 |
ss 独有且高频的三个能力值得写进运维手册:state 表达式(内核侧过滤)、-i(cwnd/rtt 直读)、-K(定向断连,需 CAP_NET_ADMIN,见 3.17 实测受限现象)。
💡 说明:迁移建议很简单——新脚本一律用 ss;只有拿到一台只有 net-tools 的老机器才回退 netstat,两者写法按上表逐行对应即可,参数语义完全等价。
四、实战示例
4.1 端口监听与进程归属一条龙
python3 -m http.server 18000 --bind 127.0.0.1 >/dev/null 2>&1 &
python3 /root/demo/ss/hold.py 18000 25 >/dev/null 2>&1 &
ss -tlp | grep 18000
LISTEN 0 5 127.0.0.1:18000 0.0.0.0:* users:(("python3",pid=8456,fd=3))
ss -tnp | grep 18000
ESTAB 0 0 127.0.0.1:50298 127.0.0.1:18000 users:(("python3",pid=8458,fd=3))
ESTAB 0 0 127.0.0.1:18000 127.0.0.1:50298 users:(("python3",pid=8456,fd=4))
一条 -tlp 确认监听与进程;一条 -tnp 看现存连接两端的进程对(8456 服务端 fd=4 / 8458 客户端 fd=3)。
4.2 连接状态分布统计
ss -tan | grep 18000 | tr -s " " | cut -d" " -f1 | sort | uniq -c
2 ESTAB
1 LISTEN
1 TIME-WAIT
压测时 ESTAB 数应约等于并发数,TIME-WAIT 堆积量反映短连接频率,一条管道即可出报表。
4.3 组合过滤:定位到具体连接
ss -tn dst 127.0.0.1 dport = :18000
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
ESTAB 0 0 127.0.0.1:50298 127.0.0.1:18000
dst 与 dport 并列即 AND,从连接表里一步筛出「对端是 127.0.0.1 且目标端口 18000」的客户端连接;换成 sport = :18000 则看服务端视角。
4.4 观察连接健康度(rtt / cwnd)
ss -ti dst 127.0.0.1
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
ESTAB 0 0 127.0.0.1:50298 127.0.0.1:18000
cubic wscale:7,7 rto:205 rtt:0.014/0.007 mss:32741 pmtu:65535 rcvmss:536 advmss:65483 cwnd:10 bytes_acked:1 segs_out:2 segs_in:1 send 187Gbps lastsnd:1064 lastrcv:1064 lastack:1064 pacing_rate 374Gbps delivered:1 app_limited rcv_space:65495 rcv_ssthresh:65495 minrtt:0.014
ESTAB 0 0 127.0.0.1:18000 127.0.0.1:50298
cubic wscale:7,7 rto:205 rtt:0.01/0.005 mss:32768 pmtu:65535 rcvmss:536 advmss:65483 cwnd:10 segs_in:2 send 262Gbps lastsnd:1064 lastrcv:1064 lastack:1064 pacing_rate 524Gbps delivered:1 app_limited rcv_space:65483 rcv_ssthresh:65483 minrtt:0.01
过滤与信息参数可叠加:-ti + dst 一步拿到目标连接的 rtt/cwnd/收发字节。巡检脚本建议固定采集 ss -ti state established 的 rtt 与 lastsnd 两个字段做趋势。
五、注意事项与常见问题
Q1:ss -K 报 SOCK_DESTROY answers: Operation not permitted?
SOCK_DESTROY 需要 CAP_NET_ADMIN 能力。实测环境 root 的 CapEff 为 0xa80425fb,不含该能力位(见 3.17 实测),因此即便 root 也被内核拒绝;安全加固基线收窄了能力位的运行环境(及 gVisor 等沙箱场景)都会出现同样报错,先 grep CapEff /proc/self/status 确认能力位再判断。
Q2:ss 显示的服务名和端口号为什么对不上我 grep 的?
ss 默认按 /etc/services 翻译端口名(22→ssh),-n 才显示数字;Ubuntu 22.04 中 8000 无登记名恒显数字(netstat 篇 3.5 实测)。写脚本判断端口一律用 ss -tln | grep -w 18000 这种数字匹配,避免两种模式混用。
Q3:ss 默认输出为什么和 netstat 不一样?
三点差异:ss 默认不含 UNIX 域套接字、不做主机名反解(更快)、状态用缩写(ESTAB/TIME-WAIT)。要 netstat 风格的全量视图用 ss -a。
Q4:ss -l 怎么多出一堆 nl/xfrm 行?
那是 netlink、xfrm 等内核通道套接字,ss 取数本身也要用到它们。加 -t/-u 限定协议族即可屏蔽,标准写法是 ss -tuln。
Q5:Recv-Q/Send-Q 和 netstat 含义一致吗? 一致。ESTAB 态是字节队列;LISTEN 态 Recv-Q=待 accept 连接数、Send-Q=listen backlog(实测 3.4 表中 sshd 为 128、http.server 为 5)。LISTEN 行 Recv-Q 长期顶满 Send-Q 就要扩 backlog 或排查应用 accept 速度。
Q6:ss -s 的 Total 和 TCP 行为什么差很多?
Total 是当前活跃套接字,TCP: 70 (estab 4, closed 58, ...) 包含已 close 但内核对象未释放的套接字与 TIME-WAIT(见 3.7 实测:Total 17 与 TCP 70 并存属正常),看并发数以括号里的 estab 为准。
Q7:state 过滤把 UNIX 套接字也带出来了?
ss state established 不加 -t 时会匹配所有族的同名状态(实测 3.15 注)。统一加 -t(或 -u)限定协议族再过滤。
六、参考链接
- ss(8) 手册:https://man7.org/linux/man-pages/man8/ss.8.html
- iproute2 源码仓库(ss 实现所在):https://git.kernel.org/pub/scm/network/iproute2/iproute2.git
- netlink(7) 手册(取数通道):https://man7.org/linux/man-pages/man7/netlink.7.html
- rtnetlink(7)(SOCK_DIAG 与 SOCK_DESTROY 消息族):https://man7.org/linux/man-pages/man7/rtnetlink.7.html
- capabilities(7)(CAP_NET_ADMIN 能力位定义):https://man7.org/linux/man-pages/man7/capabilities.7.html
- TCP 状态机(RFC 9293):https://www.rfc-editor.org/rfc/rfc9293