2547 字
约 8 分钟
17
S15-X03 · Linux 查看本机 IP 与端口占用:两条命令的完整脉络

“服务明明起来了,怎么访问不了”——这类问题九成要在两条命令之间来回对账:ip addr 回答“这台机器的网卡拿到了什么地址”,ss -tulnp 回答“到底哪个进程在监听哪个地址”。两边对不上时,问题一定出在中间:绑错网卡、监听落在回环、协议栈只开了 IPv6、或者前面还隔着一层 NAT 和容器端口映射。

这篇按“先看地址 → 再看监听 → 最后对账”的顺序拆开,顺带把 ifconfig / netstat 这对旧命令的替代关系讲清楚。


1. ip addr 与 ifconfig:一条已弃用,一条是替代品

$ ip addr show
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 52:54:00:8f:1a:2c brd ff:ff:ff:ff:ff:ff
    inet 10.0.3.15/24 brd 10.0.3.255 scope global dynamic eth0
       valid_lft 86391sec preferred_lft 86391sec
    inet6 fe80::5054:ff:fe8f:1a2c/64 scope link
维度 ip addr(iproute2) ifconfig(net-tools)
是否默认安装 是,随 iproute2 装在 Ubuntu 24.04 否,需 apt install net-tools
维护状态 活跃维护 上游事实上停止演进
能力面 地址、路由、链路、邻居、规则、隧道 只有地址与链路的一部分
依赖 netlink ioctl

替代关系不是“新命令更好看”,而是 ifconfig 调用的 ioctl 接口表达不了策略路由、多地址优先级、VRF 这些东西。写脚本时直接用:

$ ip -br addr                      # 一行一个网卡,机读友好
$ ip -j addr | jq '.[].addr_info'  # JSON 输出,配合 jq
$ ip route get 1.1.1.1             # 查“去这个目标走哪张网卡、源地址是谁”

最后一条对排障极有用:它直接给出内核选路结果,比自己猜“应该走 eth0 吧”可靠。


2. 公网 IP 为什么在 ip a 里看不到

ip a 列的永远是网卡上配置的地址。云主机多数是 NAT 出网,公网地址配在云厂商的边缘设备上,本机只有一张私网网卡,所以 ip a 里那串 10.x / 172.16.x / 192.168.x 是真实且完整的——它没漏,是本来就没有。

取公网出口地址只能“问外面”:

$ curl -s4 https://api.ipify.org; echo
$ curl -s https://ifconfig.me/ip
$ dig +short myip.opendns.com @resolver1.opendns.com
来源 反映的是什么 典型偏差
ip a 的 inet 本机网卡地址 有公网 IP 的机房会直接看到公网地址
curl ifconfig.me 出口 NAT 后的源地址 配了代理/多出口时会拿到代理的地址
云控制台的“公网 IP” 厂商侧弹性地址 可能与本机出口不一致(多网卡、双栈)
dig +short myip.opendns.com DNS 查询的源地址 只走 UDP/53,和 TCP 出口可能不同

能不能被公网直连看的是入方向:监听地址是 0.0.0.0 还是 127.0.0.1,再叠加安全组,任一不通过外面就连不上。


3. ss -tulnp 与 netstat 的替代关系

$ sudo ss -tulnp
Netid State  Recv-Q Send-Q Local Address:Port  Peer Address:Port Process
udp   UNCONN 0      0          127.0.0.54:53         0.0.0.0:*     users:(("systemd-resolve",pid=712,fd=20))
tcp   LISTEN 0      4096         0.0.0.0:80           0.0.0.0:*     users:(("nginx",pid=1204,fd=6))
tcp   LISTEN 0      128        127.0.0.1:8080       0.0.0.0:*     users:(("java",pid=5123,fd=98))
tcp   LISTEN 0      511                *:443              *:*       users:(("nginx",pid=1204,fd=7))
netstat 写法 ss 等价写法 说明
netstat -tuln ss -tuln 字母含义一致,ss 更快
netstat -tulnp ss -tulnp -p 需要 root 才能看到别人的进程
netstat -an | grep 8080 ss -tanp 'sport = :8080' 过滤表达式比 grep 精确
netstat -rn ip route 路由已迁到 ip 命令

-t TCP、-u UDP、-l 只听、-n 不解析、-p 带进程。ss 直接读 netlink,netstat 要遍历 /proc/net/tcp,连接上万时差距是秒级。


4. 按端口反查进程:lsof 与 fuser

$ sudo lsof -i:8080 -P -n
java  5123 root   98u  IPv4 23117  0t0  TCP *:8080 (LISTEN)
$ sudo fuser -v 8080/tcp
                     USER   PID ACCESS COMMAND
8080/tcp:            root  5123 F.... java
工具 强项 短板
ss -tulnp 全量一次看完、状态齐全 只看 socket,不看进程持有的其他文件
lsof -i:PORT 能同时看 LISTEN 与已建立连接,可加 -p/-u 组合 大机器上遍历 /proc 慢,需 root
fuser -v PORT/tcp 输出最短,适合塞进脚本判断 信息量少,看不到远端地址
lsof -p PID 进程维度的全部句柄 不按端口筛时噪音大

fuser 对已建立的连接也会命中,别把“在连”读成“在听”。


5. “端口被占用”其实有三种

5.1 同端口、不同绑定地址:并不冲突

$ sudo ss -tlnp 'sport = :8080'
LISTEN 0 128 127.0.0.1:8080 0.0.0.0:* users:(("java",pid=5123,fd=98))
LISTEN 0 511 0.0.0.0:8080   0.0.0.0:* users:(("nginx",pid=1204,fd=9))

能不能共存取决于绑定顺序与 SO_REUSEADDR:先绑具体地址、后绑通配地址,在通配侧开 SO_REUSEADDR 时可以共存;反过来先绑 0.0.0.0 再绑 127.0.0.1 会直接报 Address already in use。具体行为按内核版本核对;更常见的硬冲突是两个进程都绑 0.0.0.0。

5.2 TIME_WAIT 导致的“端口被占”

$ ss -tan state time-wait | wc -l
$ ss -tan state time-wait 'sport = :8080' | head

TIME_WAIT 是主动关闭方保留 2MSL(Linux 固定 60 秒)以防旧报文串进新连接,不是 bug。被动关闭的服务端一般没有;但客户端(反代上游、压测工具)跑大量短连接就会堆出几千个,再想复用同一源端口就会失败。

现象 判断 处理
ss -s 里 timewait 数万 短连接客户端行为 改用连接池/keep-alive
bind 报 EADDRINUSE 但 ss 无 LISTEN 残留 TIME_WAIT 或别的 netns ss -tan state time-wait 确认;别急着调 tcp_tw_reuse
调小 tcp_fin_timeout 无效 它管 FIN_WAIT_2,不管 TIME_WAIT TIME_WAIT 时长不可调

5.3 容器端口映射:ss 看到的可能不是你以为的进程

$ docker ps --format '{{.Names}}\t{{.Ports}}'
web   0.0.0.0:8080->8080/tcp
$ sudo ss -tlnp 'sport = :8080'
LISTEN 0 4096 0.0.0.0:8080 0.0.0.0:* users:(("docker-proxy",pid=2210,fd=4))

默认 docker-proxy(userland proxy)会在宿主上占一个监听;关掉它之后改由 iptables DNAT 转发,此时宿主 ss 里可能看不到 8080 的 LISTEN,但流量照样通。所以“ss 里没有这个端口”不能单独作为“服务没起来”的证据,必须再 docker ps 或 iptables -t nat -L -n 对一次。


6. ss 输出里的状态怎么读

State 含义 排障含义
LISTEN 正在等待入连接 服务已在听;连不上就是防火墙/路由/绑定地址问题
ESTAB 已建立 有活跃连接;数量暴涨要看应用层
SYN-SENT 已发 SYN 等 SYN-ACK 持续存在 = 对端不回或回程被丢
SYN-RECV 收到 SYN 已回 SYN-ACK 大量堆积常见于 SYN flood 或回程不通
TIME-WAIT 主动关闭后等待 见 5.2,属正常收尾
CLOSE-WAIT 对端已关,本端未 close 应用没关 fd,是代码问题,堆积会吃满句柄
FIN-WAIT-2 本端已关,等对端 FIN 长期停留说明对端异常,受 tcp_fin_timeout 影响

Recv-Q / Send-Q 在两列语义不同:LISTEN 状态下它们是已完成三次握手但未被 accept 的队列长度与 backlog 上限。Recv-Q 持续接近上限,说明应用 accept 不过来——这时去查应用,而不是查网络。

$ ss -s                                  # 全局汇总
$ ss -tan state established '( dport = :443 )' | wc -l

7. IPv6 双栈下 ss 的三种写法

输出形式 含义
0.0.0.0:80 只绑 IPv4 通配
[::]:80 绑 IPv6 通配;bindv6only=0 时同时接受 IPv4
*:80 ss 的合并显示,表示双栈通配
127.0.0.1:80 仅 IPv4 回环,localhost 解析到 ::1 时会连不上
$ ss -4 -tlnp          # 只看 IPv4
$ ss -6 -tlnp          # 只看 IPv6
$ sysctl net.ipv6.bindv6only
net.ipv6.bindv6only = 0

最容易踩的坑:程序写了 listen("::") 而 bindv6only=1,于是只服务 IPv6;客户端 curl localhost 先试 ::1 成功、curl 127.0.0.1 失败,看起来像“时好时坏”。判断方法就是分别 ss -4 与 ss -6 各看一次。


8. 排错:六种“看不到/连不上”

现象 原因 处理
ss -p 无 Process 列 非 root,无权读他人 fd sudo ss -tulnp
服务在跑但 ss 无监听 只 bind 未 listen,或崩在 listen 前 看应用日志;strace -f -e trace=network 复现
本机通、外网不通 绑了 127.0.0.1,或安全组未放行 改绑 0.0.0.0;核安全组 + ufw status
curl localhost 通、curl 127.0.0.1 不通 IPv6 单栈监听 见第 7 节
端口被占但无进程 在别的 netns/容器内 nsenter -t <pid> -n ss -tlnp
bind: address already in use 但 ss 干净 TIME_WAIT 或 SO_REUSEPORT 被占用 ss -tan state time-wait;查是否有 SO_REUSEPORT 多进程

9. 边界:这些事本就不该用 ip/ss 解决

需求 更合适的工具 理由
端口是否“从外面”可达 nc -vz / curl --connect-timeout 本机视角看不到中间链路
抓包看握手细节 tcpdump / ss -i 状态机只给结论,不给报文
云上安全组规则 厂商控制台 / API 规则不在本机内核里

一句话收口:ip a 说“我是谁”,ss -tulnp 说“我在听谁”,lsof/fuser 说“是谁在听”,curl/dig 说“外面看见的是谁”。 四个视角都对上,才叫“端口是通的”。


10. 自测

  1. ip a 里只有 10.0.3.15/24,说明这台机器没有公网 IP 吗?
  2. 写出 netstat -tulnp 的 ss 等价命令,并说明 -p 为什么常要加 sudo。
  3. 两个进程分别监听 127.0.0.1:8080 与 0.0.0.0:8080,哪个先绑会成功?
  4. ss 输出里 CLOSE-WAIT 大量堆积,指向网络问题还是应用问题?
  5. 关掉 docker-proxy 后宿主 ss 看不到映射端口,是否等于服务未启动?
  6. curl localhost 成功而 curl 127.0.0.1 失败,最可能的内核参数是什么?
S15-X03 · Linux 查看本机 IP 与端口占用:两条命令的完整脉络
https://www.clxhxhhr.top/posts/5705/
作者
clxstart
发布于
2024-12-31
许可协议
CC BY-NC-SA 4.0
评论
0 条
还没有评论,先写一条吧。