「端口被占用了」「服务起不来报地址已被使用」「防火墙开了但连不上」------端口问题几乎每个部署日都会遇到。排查端口,需要回答四个问题:谁在监听、端口通不通、谁占用了、连接状态如何 。这一篇用 ss、lsof、nc、telnet、curl 五把工具,串成一套端口排查的完整流水线,并讲清端口占用、连接拒绝、超时这几类问题的处理套路。
一、端口排查的四个问题
拿到一个端口号,先想清楚要查什么:
| 问题 | 工具 |
|---|---|
| 谁在监听这个端口? | ss -lntp / lsof -i |
| 端口从外面能连吗? | nc / telnet |
| 端口被谁占用了(起不来服务)? | lsof -i / fuser |
| 应用层返回什么? | curl |
思路:先看本机有没有监听 → 再看对方能不能连 → 再看应用是否正常。 逐层排除。
二、ss:端口与连接的首选工具
ss(socket statistics)是现代 Linux 看端口和连接的首选,比 netstat 快得多(第 67 篇已介绍),这里聚焦「端口排查」的用法。
查看监听端口
bash
sudo ss -tlnp
text
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))
-tTCP,-l监听,-n数字,-p显示进程0.0.0.0:80监听所有网卡;127.0.0.1:3306只监听本地(外部连不上!)
⚠️ 监听地址决定能否被外部访问
这是「端口开着却连不上」的头号原因:
| 监听地址 | 含义 |
|---|---|
0.0.0.0:80 或 *:80 |
所有网卡,外部可连 |
127.0.0.1:3306 |
仅本机回环,外部连不上 |
192.168.1.10:80 |
仅该网卡 |
看到服务只监听 127.0.0.1,那不管防火墙怎么放行,外部都连不上------要改服务的绑定配置(如 MySQL 的 bind-address、Redis 的 bind)。
精简输出
bash
# 只看监听端口,不显示进程
ss -tln
# 简洁格式
ss -tln | awk '{print $4}' | sort -t: -k2 -n
三、lsof:从进程反查端口
ss 是从「端口」找进程,lsof 反过来:从「进程」找它打开的端口和文件。
查看端口被谁占用
bash
sudo lsof -i :8080
text
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
java 12345 app 42u IPv6 1234567 0t0 TCP *:8080 (LISTEN)
一眼看出 PID 12345 的 java 占了 8080。
常用查询
bash
# 所有网络连接
sudo lsof -i
# 所有 TCP
sudo lsof -i TCP
# 某进程打开的所有文件/端口
sudo lsof -p 12345
# 某用户打开的网络连接
sudo lsof -i -u app
# 某状态
sudo lsof -i -sTCP:LISTEN
fuser:更快的端口占用查询
bash
sudo fuser 8080/tcp
text
8080/tcp: 12345
fuser 只输出 PID,速度更快,适合脚本。
四、nc / telnet:测试端口连通性
本机监听正常,还得测「从外部/另一端能不能连」。这是区分「服务问题」和「网络/防火墙问题」的关键。
nc(netcat)
bash
# 测单端口
nc -zv 192.168.1.100 80
# 测一组端口
nc -zv 192.168.1.100 20-80
# 带超时(避免长时间卡住)
nc -zv -w 3 192.168.1.100 80
# 从本机某个源端口测(排查源端口限制)
nc -zv -s 192.168.1.50 192.168.1.100 80
text
Connection to 192.168.1.100 80 port [tcp/http] succeeded!
telnet
bash
telnet 192.168.1.100 80
Connected to ...→ 端口开放Connection refused→ 端口没监听或被 REJECT- 长时间无响应 → 防火墙 DROP 或网络不通
⚠️ 三种结果对应三类问题
| 结果 | 含义 | 排查方向 |
|---|---|---|
succeeded / Connected |
端口可连 | 网络和端口都 OK,查应用 |
Connection refused |
端口没监听 | 服务没起 / 监听地址错 / 被 REJECT |
| 超时无响应 | 包被丢弃 | 防火墙 DROP / 网络不通 / 路由问题 |
refused 和超时的区别至关重要:refused 说明包到了目标、只是没人接;超时说明包根本没到。后者才是网络/防火墙问题。
用 nc 模拟服务端(测反向)
bash
# 在 A 机监听
nc -l 9999
# 在 B 机连接
nc 192.168.1.10 9999
# 直接打字,A 机能收到,说明双向通畅
五、curl:HTTP 层诊断
端口通了,还要确认 HTTP 应用正常。curl 是最实用的 HTTP 测试工具。
基础测试
bash
# 请求并显示响应头
curl -I http://192.168.1.100/
# 显示详细过程
curl -v http://192.168.1.100/
# 跟随重定向
curl -L http://example.com/
# 指定方法
curl -X POST -d '{"key":"value"}' -H "Content-Type: application/json" http://host/api
# 只看 HTTP 状态码
curl -o /dev/null -s -w "%{http_code}\n" http://host/
用 -w 拆解各阶段耗时
bash
curl -o /dev/null -s -w "dns:%{time_namelookup} connect:%{time_connect} tls:%{time_appconnect} ttfb:%{time_starttransfer} total:%{time_total}\n" https://example.com/
text
dns:0.011 connect:0.032 tls:0.098 ttfb:0.145 total:0.156
各阶段含义:
| 指标 | 含义 |
|---|---|
time_namelookup |
DNS 解析耗时 |
time_connect |
TCP 连接耗时 |
time_appconnect |
TLS 握手完成耗时 |
time_starttransfer |
首字节时间(TTFB) |
time_total |
总耗时 |
哪个阶段大,问题就在哪:DNS 慢查 DNS,connect 慢查网络,ttfb 慢查应用。
常用诊断选项
bash
# 忽略证书(测试自签名)
curl -k https://host/
# 指定 Host 头(测虚拟主机)
curl -H "Host: example.com" http://1.2.3.4/
# 指定网卡/源 IP
curl --interface eth0 http://host/
# 显示响应时间
curl -w "@curlformat.txt" -o /dev/null -s http://host/
六、端口被占用怎么处理
服务起不来报 Address already in use,处理步骤:
bash
# 1. 找到占用端口的进程
sudo lsof -i :8080
# 或
sudo ss -lntp | grep :8080
# 或
sudo fuser 8080/tcp
# 2. 确认是不是僵尸进程或重复启动
ps -fp <PID>
# 3. 决定杀掉还是换端口
sudo kill <PID> # 优雅终止
sudo kill -9 <PID> # 强杀(谨慎)
# 4. 确认端口释放
sudo ss -lntp | grep :8080
⚠️ TIME_WAIT 导致端口"被占用"
服务重启后立刻报端口占用,可能是上一个连接处于 TIME_WAIT:
bash
ss -tan | grep :8080 | grep TIME-WAIT
处理:服务端应设 SO_REUSEADDR(大多数框架默认开启),或等待 60 秒(2MSL)自然释放。
七、⚠️ 常见错误
- 只看本机监听说"端口开了" 。还要从对端
nc/telnet测,本机监听的127.0.0.1外部连不上。 refused和超时不分。refused 是服务没起(本地问题),超时是网络/防火墙(链路问题),方向完全不同。- 忽略了监听地址 。服务绑在
127.0.0.1,却一直查防火墙。 - lsof 看不到进程。需要 root,普通用户看不到别人的进程。
- curl 不指定 Host 测虚拟主机失败。多站点服务器要根据 Host 头路由,直接 IP 访问可能返回默认站或 404。
- 测端口不带超时 。
nc/telnet遇 DROP 会卡很久,加-w 3。 - 防火墙和安全组只看一个。云服务器有安全组 + 本机防火墙两层,都要放行。
curl正常但浏览器不行。可能是代理、证书、DNS 缓存等客户端侧问题。
八、原理探究:端口、套接字与四元组
端口是什么
IP 地址定位「哪台机器」,端口定位「机器上的哪个进程」。端口是 16 位,范围 0~65535:
| 范围 | 用途 |
|---|---|
| 0~1023 | 众所周知端口(需 root 绑定) |
| 1024~49151 | 注册端口 |
| 49152~65535 | 动态/临时端口(客户端出站用) |
套接字与四元组
一条 TCP 连接由四元组唯一标识:
(源IP, 源端口, 目标IP, 目标端口)
ss 里每一个连接就是一条四元组。这也是为什么服务端 0.0.0.0:80 能同时接受成千上万个连接------每个客户端的四元组不同,内核靠四元组区分它们。
客户端临时端口
客户端发起连接时,内核从「临时端口范围」里分配一个源端口:
bash
cat /proc/sys/net/ipv4/ip_local_port_range
# 32768 60999
高并发短连接场景下,这不到 3 万个端口可能被耗尽 (大量 TIME_WAIT 占着),表现为「Cannot assign requested address」。解法:开启 net.ipv4.tcp_tw_reuse(客户端侧)、用长连接、或调大端口范围。
监听套接字 vs 连接套接字
- 监听套接字 :
ss -l看到的LISTEN,服务端listen()创建的,负责接受新连接。 - 连接套接字:三次握手完成后,内核为每条连接创建一个新的套接字,由四元组标识。
同一个服务端端口(如 80)对应一个监听套接字 + N 个连接套接字 ,这就是 ss -tln 只有一条 80、而 ss -tan 有大量 80 连接的原因。
理解了模型,排查就清晰了
- 服务起不来 → 看监听套接字能否创建(端口被占、权限不足)
- 外部连不上 → 看监听地址 + 防火墙 + 安全组
- 连上没响应 → 看连接套接字状态(SYN_RECV 卡住 = 网络问题)
- 连接太多 → 看四元组数量、临时端口、TIME_WAIT
九、端口排查速查表
bash
# 1. 谁在监听端口
sudo ss -tlnp | grep :80
sudo lsof -i :80
# 2. 端口被谁占用
sudo lsof -i :8080
sudo fuser 8080/tcp
# 3. 从外面测端口
nc -zv -w3 host 80
telnet host 80
# 4. HTTP 层测试
curl -I http://host/
curl -o /dev/null -s -w "%{http_code}\n" http://host/
curl -w "dns:%{time_namelookup} connect:%{time_connect} total:%{time_total}\n" -s -o /dev/null http://host/
# 5. 看连接状态汇总
ss -tan | awk 'NR>1{print $1}' | sort | uniq -c | sort -rn
# 6. 杀占用端口的进程
sudo kill $(sudo lsof -t -i :8080)
端口排查的主线记住:ss 看谁在听 → lsof 看谁占了 → nc/telnet 测通不通 → curl 测应用 。而绝大多数「端口问题」,根因就三个:服务没起、监听地址错、防火墙没放行------对照结果类型(refused vs 超时)就能快速定位。