﻿# ss —— 查看套接字/网络连接状态（netstat 的现代替代）

## 这个工具是做什么的

`ss` = socket statistics，直接从内核读取（不像 `netstat` 那样遍历 `/proc/net`），在连接数多时快几个数量级。用来排查：连接数暴涨、端口被谁占、TCP 状态堆积（TIME-WAIT/CLOSE-WAIT）、连接泄漏、握手异常。

## 可以回答什么问题

| 问题 | 怎么用 |
|------|--------|
| 系统有多少个 TCP 连接？各状态分别多少？ | `ss -s` 看汇总 |
| 哪个进程监听了某端口？端口被占了吗？ | `ss -tlnp | grep :8080` |
| TIME-WAIT 连接多不多（短连接问题）？ | `ss -tan state time-wait | wc -l` |
| CLOSE-WAIT 堆积了吗（连接泄漏）？ | `ss -tan state close-wait`：堆积 = 程序忘了 close |
| 接收队列积压了吗（应用处理不过来）？ | `Recv-Q` 持续增大 |
| 某进程连了哪些地址？ | `ss -tanp | grep <PID>` |
| TCP 重传、RTT 多少？ | `ss -ti` 看内部信息 |

## 数据来源

- **来源接口**：内核 **netlink** 的 `NETLINK_SOCK_DIAG`（`inet_diag`）接口，直接向内核批量查询套接字表。
- **采集方式**：一次 netlink 请求即可拿到全部匹配的套接字，而 `netstat` 是逐行解析 `/proc/net/tcp` 等文本文件。
- **由此决定的特性**：连接数上万/十万时，`ss` 比 `netstat` 快**几个数量级**（少了逐行文本解析）；且 `-i`/`-e`/`-m` 能拿到 `/proc/net` 文本给不出的内核内部信息（rtt、cwnd、内存占用等）。

```bash
# iproute2 包，多数系统自带
yum install iproute / apt install iproute2
```

## 一、参数速记

`t`=TCP `u`=UDP `x`=Unix域 `l`=只看 LISTEN `a`=全部(含监听) `n`=数字不解析（快）`p`=显示进程(需 root) `s`=汇总 `i`=TCP 内部信息 `e`=扩展信息 `m`=内存用量 `o`=计时器。

## 二、常用组合

```bash
ss -tan                  # 所有 TCP 连接（数字形式）
ss -tanp                 # + 占用连接的进程/PID（root 才看得全）
ss -tlnp                 # 所有监听中的 TCP 端口 + 进程（查服务起没起/端口冲突）
ss -tunlp                # TCP+UDP 所有监听端口 + 进程
ss -uanp                 # UDP 全部
ss -s                    # 汇总：各状态连接总数，快速看规模
ss -tanp | grep <PID>    # 某进程建立/监听了哪些连接
```

## 三、输出与字段

```bash
State      Recv-Q  Send-Q   Local Address:Port   Peer Address:Port  Process
ESTAB      0       0        10.0.0.2:22          10.0.0.9:53344     users:(("sshd",pid=1234,fd=3))
LISTEN     0       128      0.0.0.0:8080         0.0.0.0:*          users:(("java",pid=5678,fd=42))
```

| 列 | 含义 | 判读 |
|----|------|------|
| `State` | TCP 状态 | 见下表 |
| `Recv-Q` | 接收队列积压字节 | LISTEN 态下是**全连接队列当前长度**；ESTAB 下是**已收到但应用还没 read 的数据**——持续大 = 应用消费不过来 |
| `Send-Q` | 发送队列 | LISTEN 态下是**全连接队列上限**（backlog）；ESTAB 下是**已发但对端还没 ACK 的数据**——持续大 = 对端慢/网络拥塞 |
| `Local/Peer Address:Port` | 本端/对端地址 | `*` 表示任意 |
| `Process` | 占用的进程（需 `-p` + root）| 定位归属 |

## 四、按条件过滤

```bash
ss -tan state established              # 只看已建立
ss -tan state time-wait                # 只看 TIME-WAIT
ss -tan state syn-recv                 # 半连接（SYN flood 时暴涨）
ss -tanp '( dport = :3306 or sport = :3306 )'   # 按端口（如 MySQL）
ss -tanp dst 10.0.0.5                  # 到某目标 IP
ss -tanp dst 10.0.0.0/24               # 到某网段
ss -tin                                # -i 显示 rtt、cwnd、重传等 TCP 内部信息
ss -tm                                 # -m 看每个 socket 的内存占用
ss -to                                 # -o 显示 keepalive/重传计时器
```

## 五、TCP 状态与排查

| 状态 | 含义 | 排查要点 |
|------|------|---------|
| `LISTEN` | 监听中 | 服务没起来先确认这个在不在 |
| `ESTAB` | 已建立 | 连接数暴涨 → 查是否连接泄漏、连接池配置 |
| `TIME-WAIT` | 主动关闭方等 2MSL | **大量堆积 = 短连接过多**：改长连接/连接池，或调 `net.ipv4.tcp_tw_reuse`、加大端口范围 `ip_local_port_range` |
| `CLOSE-WAIT` | 对端已关，本端没 `close()` | **堆积 = 程序 BUG**：代码收到 EOF 后忘了关 socket，导致 fd 泄漏，最终 `EMFILE`。这是最该警惕的状态 |
| `SYN-SENT` | 本端发了 SYN 等回应 | 大量堆积 → 连不上对端（对端挂了/防火墙丢包）|
| `SYN-RECV` | 收到 SYN 等 ACK | 大量堆积 → 可能 SYN flood，或全连接队列满 |
| `FIN-WAIT-1/2` | 关闭流程中 | 大量 FIN-WAIT-2 → 对端不发 FIN，可能对端有 bug |

## 六、典型场景

```bash
ss -s                                   # 先看总量，连接是否异常多
ss -tan state close-wait | wc -l        # CLOSE-WAIT 计数，排查连接泄漏
ss -tan state time-wait | wc -l         # TIME-WAIT 计数，判断短连接压力
ss -tlnp | grep :8080                   # 8080 被哪个进程占（端口冲突）
ss -tanp | grep -c ESTAB                # 当前建立的连接总数
watch -n1 'ss -s'                        # 持续观察连接规模变化
```

## 七、关键指标速查

| 指标 | 缩写/英文 | 命令 | 正常值 | 异常信号 |
|------|----------|------|--------|---------|
| 接收队列积压 | Recv-Q | `ss -tanp` | 0 | 持续 > 0 → 应用消费跟不上 |
| 发送队列积压 | Send-Q | `ss -tanp` | 0 | 持续 > 0 → 对端窗口关闭/拥塞 |
| LISTEN backlog 当前 | Recv-Q (LISTEN) | `ss -tlnp` | 0 | > 0 → 全连接队列积压 |
| LISTEN backlog 上限 | Send-Q (LISTEN) | `ss -tlnp` | `somaxconn` | 太小 → 高并发下 SYN 被丢弃 |
| ESTABLISHED 总数 | Estab Count | `ss -s` | 视业务 | 接近 somaxconn → 受限 |
| TIME-WAIT 数 | TIME-WAIT Count | `ss -tan state time-wait \| wc -l` | < 1000 | > 万 → 短连接过多 |
| CLOSE-WAIT 数 | CLOSE-WAIT Count | `ss -tan state close-wait \| wc -l` | 0 或很少 | 堆积 → 连接泄漏（程序忘 close） |
| SYN-RECV 数 | SYN-RECV Count | `ss -tan state syn-recv \| wc -l` | 很少 | 暴涨 → SYN flood 攻击或全连接队列满 |
| RTT | Round-Trip Time | `ss -ti` | < 10ms（同机房） | 几百 ms → 跨地域/拥塞 |
| TCP 重传 | Retrans | `ss -ti \| grep retrans` | 0 | > 0 → 丢包/网络质量差 |
| 拥塞窗口 | Cwnd | `ss -ti \| grep cwnd` | 视 RTT | `ssthresh` 被压低 → 曾发生拥塞 |
| Socket 内存占用 | Socket Mem | `ss -tm` | 视场景 | 持续增长 → socket buffer 泄漏 |

### 指标体系全景图

```plantuml
@startuml
skinparam shadowing false
skinparam classBorderColor #455A64
skinparam classBackgroundColor #ECEFF1
skinparam packageBorderColor #37474F

package "ss 可观察指标" as SS #F5F5F5 {

  package "连接状态维度" as CONN_STATE #BBDEFB {
    class "ESTABLISHED 总数" as ESTAB {
      + 命令: ss -s
      + 异常: 接近 somaxconn → 受限
    }
    class "LISTEN" as LISTEN {
      + 命令: ss -tlnp
      + 异常: 无 → 服务未启动
    }
    class "TIME-WAIT" as TW {
      + 命令: ss -tan state time-wait
      + 异常: > 万 → 短连接过多
    }
    class "CLOSE-WAIT" as CW {
      + 命令: ss -tan state close-wait
      + 异常: 堆积 → 连接泄漏
    }
    class "SYN-SENT" as SSENT {
      + 命令: ss -tan state syn-sent
      + 异常: 堆积 → 对端不可达
    }
    class "SYN-RECV" as SRECV {
      + 命令: ss -tan state syn-recv
      + 异常: 暴涨 → SYN flood
    }
    class "FIN-WAIT-1/2" as FW {
      + 命令: ss -tan state fin-wait-1
      + 异常: 大量 FIN-WAIT-2 → 对端不发 FIN
    }
  }

  package "队列积压维度" as QUEUE #C8E6C9 {
    class "Recv-Q（ESTAB）" as RECVQ {
      + 命令: ss -tanp
      + 含义: 已收到但应用未 read
      + 异常: 持续 > 0 → 应用消费跟不上
    }
    class "Send-Q（ESTAB）" as SENDQ {
      + 命令: ss -tanp
      + 含义: 已发但对端未 ACK
      + 异常: 持续 > 0 → 对端慢/拥塞
    }
    class "Recv-Q（LISTEN）" as RECVQ_L {
      + 命令: ss -tlnp
      + 含义: 全连接队列当前长度
      + 异常: > 0 → backlog 积压
    }
    class "Send-Q（LISTEN）" as SENDQ_L {
      + 命令: ss -tlnp
      + 含义: 全连接队列上限（backlog）
      + 异常: 太小 → SYN 被丢弃
    }
  }

  package "TCP 内部信息维度" as TCP_INTERN #FFE0B2 {
    class "RTT" as RTT {
      + 命令: ss -ti
      + 异常: > 几百 ms → 跨地域/拥塞
    }
    class "重传次数" as RETRANS {
      + 命令: ss -ti | grep retrans
      + 异常: > 0 → 丢包/网络质量差
    }
    class "拥塞窗口 cwnd" as CWND {
      + 命令: ss -ti | grep cwnd
      + 异常: ssthresh 被压低 → 曾拥塞
    }
    class "计时器" as TIMER {
      + 命令: ss -to
      + 内容: keepalive / 重传计时器
    }
  }

  package "Socket 内存维度" as SOCK_MEM #F8BBD0 {
    class "Socket 内存占用" as SMEM {
      + 命令: ss -tm
      + 异常: 持续增长 → buffer 泄漏
    }
  }

}

CONN_STATE -[hidden]right-> QUEUE
QUEUE -[hidden]right-> TCP_INTERN
TCP_INTERN -[hidden]right-> SOCK_MEM
@enduml
```

## 八、常见排查方法与分析

### 8.1 CLOSE-WAIT 堆积 → 连接泄漏（最危险）

```bash
ss -tan state close-wait    # 堆积大量连接
  → ss -tanp state close-wait | awk '{print $NF}' | sort | uniq -c
    → 找到占用的进程/PID
      → lsof -p <PID> | wc -l → 看 fd 总量
        → 检查代码：对端 FIN 后是否忘了 close()
```

### 8.2 TIME-WAIT 过多 → 短连接

```bash
ss -s → timewait 数万
  → ss -tan state time-wait | head → 看主要涉及哪些端口
    → 短连接过多（每次请求建立新连接而非复用）
      → 改为长连接/连接池
      → 调内核参数：tcp_tw_reuse=1, ip_local_port_range
```

### 8.3 Recv-Q 积压 → 应用处理不过来

```bash
ss -tanp → Recv-Q 持续 > 0
  → 数据已到达内核，但应用程序没调用 read()/recv()
    → top 看进程 CPU 是否打满
    → strace -e trace=read,recvfrom -p <PID> 看是否在调用
```

### 8.4 连接数暴涨定位

```bash
ss -s → 连接总数异常多
  → ss -tan state established | awk '{print $6}' | sort | uniq -c | sort -rn
    → 看连接的主要目标 → 可能是某个下游服务慢导致堆积
    → 或看是否有连接泄漏（增长不回落）
```

### 8.5 网络延迟/重传定位

```bash
ss -ti → 看 rtt 和 retrans
  → rtt 高 → 链路上有延迟（跨机房/拥塞）
  → retrans 非零 → 丢包
    → sar -n ETCP 1 → 看全系统重传速率
    → ethtool -S <网卡> → 查硬件层丢包
```

## 九、交叉引用

- **进程 fd 检查**：[lsof](/tools/network/lsof.md)（ss 看到连接多 → lsof 确认 fd 数）
- **网络中断**：[mpstat](/tools/cpu/mpstat.md) `%soft`（Recv-Q 高 + 软中断偏单核）
- **数据源**：[procfs](/tools/proc/procfs.md) §三（/proc/net/* 和 netlink 接口）
- **抓包分析**：[tcpdump](https://www.tcpdump.org/)（比 ss 更底层的包级分析）

## 十、与其他工具联动

- **软中断 `%si` 高 / 网络卡顿**：`ss -s` 看连接规模 → `mpstat -P ALL 1` 看 `%soft` 是否压在某个核 → `cat /proc/softirqs` 看 `NET_RX/NET_TX` 增长 → 考虑网卡多队列/RPS。
- **`Recv-Q` 持续积压**：应用处理不过来，回到 `top`/`perf` 看应用为何慢。
- **fd 耗尽（EMFILE）**：`ss` 数连接 + `lsof -p <PID>` 数 fd，交叉确认是不是连接没关。

