FAQ-12:连接被拒绝(connection refused)
预计阅读时间:19 分钟
📖 目录
问题速查表
| 问题 | 解决章节 |
|---|---|
| 目标端口没有进程在监听 | 第一步:确认目标端口是否有进程监听、启动或重启服务 |
| 防火墙规则主动拒绝(REJECT)连接 | 第二步:检查防火墙规则、配置防火墙放行规则 |
| 服务只绑定 127.0.0.1,外部无法连接 | 第三步:检查服务绑定地址、修改服务监听地址 |
| 客户端无法确认端口连通性 | 第四步:测试端口连通性 |
| Docker 容器端口映射不正确 | 第六步:检查 Docker 容器端口映射、修复 Docker 端口映射 |
| SELinux/AppArmor 阻止服务监听端口 | 原因分析 |
Connection refused 是网络故障排查中最常见的错误之一。当你尝试连接远程服务器的某个端口时,如果收到这个错误,意味着客户端成功发送了 TCP SYN 连接请求,但目标主机在该端口上没有进程在监听,或者防火墙主动发送了 RST(重置)包拒绝了你的连接。这个错误在日志中通常表现为 connect to IP port port: Connection refused。
理解 Connection refused 的关键在于区分它与其他网络错误的不同。它说明网络层是通的(IP 可达、路由正常),但应用层没准备好(服务未启动、未监听正确端口)或被主动拒绝(防火墙规则、安全组策略)。这与 Connection timed out(网络不通或被 DROP)和 Network unreachable(路由不可达)有本质区别。
这个错误在各种场景中都会出现:部署新服务后端口配置错误、MySQL/Redis 远程连接失败、Docker 容器端口映射问题、Nginx 反向代理目标不可达、防火墙规则变更后服务中断等。系统性地排查 Connection refused 需要从服务端监听状态、防火墙规则、服务绑定地址三个维度入手。掌握正确的排查顺序和工具使用方法,可以快速定位并解决问题。
原因分析
服务未启动或未正常运行是最常见的原因。目标端口上没有进程在监听,客户端的连接请求自然被拒绝。服务可能因为配置错误、依赖缺失、资源不足等原因启动失败。
服务绑定地址错误也会导致此问题。如果服务只绑定在 127.0.0.1(本地回环地址),外部客户端就无法连接。这在 MySQL、Redis 等数据库服务的默认配置中非常常见,它们出于安全考虑默认只监听本地。
防火墙规则是另一个主要原因。防火墙可以配置两种拒绝方式:REJECT(主动返回 RST 包,客户端收到 Connection refused)和 DROP(静默丢弃,客户端连接超时)。REJECT 模式下客户端会立即收到拒绝响应。
Docker 容器网络隔离在容器化环境中很常见。容器内的服务可能监听了容器内部的端口,但没有正确映射到宿主机端口。此外,容器之间默认使用桥接网络,直接使用 localhost 连接会失败。
SELinux/AppArmor 安全策略也可能阻止服务监听特定端口,即使服务配置本身是正确的。这在 CentOS/RHEL 系统上尤为常见。
排查决策树
Connection refused 的排查遵循"从本机到跨机、从服务到防火墙"的分层思路,按下面的决策树逐层排除:
连接报错:Connection refused
│
├─ ① 先在本机测试(排除网络因素)
│ │ curl/nc 127.0.0.1 端口
│ │ ├─ 本机也 refused → 服务没起或没在监听 → 进入 ②
│ │ └─ 本机正常 → 进入 ③
│
├─ ② 检查服务状态与监听地址
│ │ ss -tlnp | grep 端口
│ │ ├─ 无输出 → systemctl status 服务 → 启动 / 查日志
│ │ └─ 有输出但显示 127.0.0.1:端口 → 只绑定本机
│ │ └─ 修改服务监听地址为 0.0.0.0 并重启
│
├─ ③ 跨机测试
│ │ nc -zv 目标IP 端口
│ │ ├─ 得到 refused → 网络层是通的,被主动拒绝 → 进入 ④
│ │ └─ 得到 timeout → 网络不通或被 DROP → 查防火墙 DROP 规则、
│ │ 安全组、路由、目标主机网络状态
│
└─ ④ 检查防火墙与服务侧安全策略
├─ nft list ruleset / ufw status / firewall-cmd --list-all
│ └─ 找到 REJECT 规则 → 放行目标端口
├─ SELinux/AppArmor 是否阻止(ausearch -m avc / aa-status)
└─ Docker 场景:docker ps 检查端口映射是否缺失
排查步骤
第一步:确认目标端口是否有进程监听
# 在目标服务器上执行
ss -tlnp | grep :端口号
# 或使用 lsof
lsof -i :端口号
# 或使用 netstat(已废弃,需安装 net-tools,建议优先使用 ss)
netstat -tlnp | grep :端口号
如果以上命令没有输出,说明该端口确实没有进程在监听。需要检查服务是否已启动:systemctl status 服务名。
第二步:检查防火墙规则
# 查看 nftables 规则(现代系统推荐)
sudo nft list ruleset
# 查看 iptables 规则
sudo iptables -L -n -v
# 查看 ufw 状态(Ubuntu)
sudo ufw status verbose
# 查看 firewalld 状态(CentOS/RHEL)
sudo firewall-cmd --list-all
注意区分 REJECT 和 DROP:REJECT 会返回 RST 包,客户端收到 Connection refused;DROP 则静默丢弃,客户端连接超时。检查是否有针对目标端口的 REJECT 规则。
第三步:检查服务绑定地址
ss -tlnp | grep 服务名
# 如果显示 127.0.0.1:端口,服务只监听本地
# 需要改为 0.0.0.0:端口 才能接受外部连接
# 例如 MySQL 配置
cat /etc/mysql/mysql.conf.d/mysqld.cnf | grep bind-address
# 默认:bind-address = 127.0.0.1
# 改为:bind-address = 0.0.0.0
第四步:测试端口连通性
# 从客户端测试(推荐使用 nc)
nc -zv 目标IP 端口
# 使用 telnet
telnet 目标IP 端口
# 使用 curl 测试 HTTP/HTTPS
curl -v http://目标IP:端口
# 使用 nmap 扫描端口状态
nmap -p 端口 目标IP
第五步:检查服务日志
# 查看服务最近的日志
sudo journalctl -u 服务名 -n 50 --no-pager
# 查看系统日志中的连接拒绝记录
sudo dmesg | tail -20
sudo grep -i "refused\|reject" /var/log/syslog
第六步:检查 Docker 容器端口映射
# 查看容器端口映射
docker ps --format "table {{.Names}}\t{{.Ports}}"
# 确认容器内服务监听地址
docker exec 容器名 ss -tlnp
# 测试容器网络连通性
docker exec -it 另一个容器名 ping 目标容器名
# 查看 Docker 网络配置
docker network ls
docker network inspect bridge
解决方案
启动或重启服务
# 启动服务
sudo systemctl start 服务名
# 设置开机自启
sudo systemctl enable 服务名
# 查看服务状态
sudo systemctl status 服务名
修改服务监听地址
# 以 MySQL 为例
# 编辑配置文件
sudo vim /etc/mysql/mysql.conf.d/mysqld.cnf
# 修改 bind-address 为 0.0.0.0
# 重启服务
sudo systemctl restart mysql
# 验证
ss -tlnp | grep 3306
配置防火墙放行规则
# UFW(Ubuntu)
sudo ufw allow 3306/tcp
sudo ufw reload
# nftables
sudo nft add rule inet filter input tcp dport 3306 accept
# iptables
sudo iptables -A INPUT -p tcp --dport 3306 -j ACCEPT
# firewalld(CentOS)
sudo firewall-cmd --permanent --add-port=3306/tcp
sudo firewall-cmd --reload
修复 Docker 端口映射
# 重新创建容器并映射端口
docker run -d -p 3306:3306 --name mysql-container mysql:8
# 或修改 docker-compose.yml
# ports:
# - "3306:3306"
常见场景速查
| 场景 | 原因 | 解决方案 |
|---|---|---|
| 服务刚重启完连不上 | 进程还没完全启动 | 等几秒重试,用 systemctl status 确认 |
| 远程连 MySQL/Redis 失败 | 服务只绑定 127.0.0.1 | 修改 bind-address 为 0.0.0.0 |
| 防火墙开启后连不上 | 防火墙规则拒绝了端口 | 添加放行规则:ufw allow 端口 |
| 容器内连宿主机失败 | 容器网络隔离 | 用宿主机 IP(通常是 172.17.0.1)而非 localhost |
| HTTPS 连接被拒 | 443 端口未开放或证书问题 | 检查防火墙和 Nginx/Apache SSL 配置 |
| SELinux 阻止服务 | 安全策略限制 | sudo setsebool -P httpd_can_network_connect 1 |
真实案例
案例 A:服务只绑定 127.0.0.1,外部连接一律 refused
现象:开发者在服务器(10.0.0.5)上启动了 Node.js API 服务(端口 8080),本机 curl 一切正常,但远程客户端始终连不上:
# 远程客户端
$ curl -v http://10.0.0.5:8080/api/health
* Trying 10.0.0.5:8080...
* connect to 10.0.0.5 port 8080 failed: Connection refused
* Failed to connect to 10.0.0.5 port 8080 after 0 ms
# 服务器本机(居然正常)
$ curl http://127.0.0.1:8080/api/health
{"status":"ok"}
排查过程:本机可访问、远程 refused,第一反应是查服务监听地址:
$ ss -tlnp | grep 8080
LISTEN 0 511 127.0.0.1:8080 0.0.0.0:* users:(("node",pid=18234,fd=21))
根因:ss 输出的监听地址是 127.0.0.1:8080,服务只绑定了回环接口,外部流量根本无法到达。代码里写死了绑定地址:
app.listen(8080, '127.0.0.1'); // 错误:只监听本机回环
修复:改为监听所有接口(或按安全要求绑定业务网卡的具体 IP):
app.listen(8080, '0.0.0.0'); // 监听所有接口
# systemd 部署场景:修改 unit 文件中的 ExecStart 参数后
sudo systemctl daemon-reload && sudo systemctl restart myapp
验证:
$ ss -tlnp | grep 8080
LISTEN 0 511 0.0.0.0:8080 0.0.0.0:* users:(("node",pid=18234,fd=21))
$ curl http://10.0.0.5:8080/api/health # 远程访问成功
{"status":"ok"}
注意:对公网暴露服务前务必先确认防火墙规则,避免端口直接裸露在公网上。
案例 B:防火墙 REJECT 与服务未启动的报错辨析
现象:两台服务器 A(10.0.0.10)访问 B(10.0.0.11)的 3306 端口失败,运维在 A 上连续做了三组测试,得到三类截然不同的报错:
# 测试一:B 上防火墙有 REJECT 规则(服务其实在监听)
$ nc -zv 10.0.0.11 3306
nc: connect to 10.0.0.11 port 3306 (tcp) failed: Connection refused
# 测试二:B 上防火墙 DROP(服务未启动)
$ nc -zv 10.0.0.11 3306
nc: connect to 10.0.0.11 port 3306 (tcp) timed out
# 测试三:B 上无防火墙规则(服务未启动)
$ nc -zv 10.0.0.11 3306
nc: connect to 10.0.0.11 port 3306 (tcp) failed: Connection refused
根因辨析:测试一和测试三的报错文字完全相同,但原因截然不同,必须靠服务端证据区分:
# 测试一对应的服务端检查:服务在监听,是防火墙拒绝
$ ss -tlnp | grep 3306
LISTEN 0 70 0.0.0.0:3306 ... # 服务其实活着
$ sudo nft list ruleset | grep 3306
tcp dport 3306 reject # ← 罪魁祸首:REJECT 规则
# 测试三对应的服务端检查:服务根本没起来
$ ss -tlnp | grep 3306 # 无任何输出
$ systemctl status mysql
● mysql.service - MySQL Community Server
Loaded: failed (Result: exit-code)
修复:测试一执行放行命令 sudo nft add rule inet filter input tcp dport 3306 accept(或对应 ufw/firewalld 命令);测试三先修复 MySQL 配置再 systemctl start mysql。
关键结论:Connection refused 只说明"连接被拒绝"——服务未启动、防火墙 REJECT 都会产生它;Connection timed out 说明"被丢弃"——防火墙 DROP、安全组、路由问题。判断依据永远是先看服务端 ss -tlnp,再对照防火墙规则,两者结合才能定性。
案例 C:Docker 容器端口映射遗漏导致外部无法访问
现象:Docker 容器内 Nginx 正常运行,curl localhost:80 返回 200,但外部访问服务器 IP:8080 报 Connection refused。
# 容器内测试正常
$ docker exec web curl -s localhost:80
<html>...nginx welcome page...</html>
# 外部访问失败
$ curl http://10.0.0.5:8080
curl: (7) Failed to connect to 10.0.0.5 port 8080: Connection refused
# 检查容器端口映射
$ docker port web
# 无输出——没有端口映射!
$ docker ps
CONTAINER ID IMAGE PORTS NAMES
a1b2c3d4e5f6 nginx web
根因:docker run 时没有使用 -p 参数映射端口,容器内的 80 端口没有暴露到宿主机。
修复:重新创建容器并添加端口映射:docker run -d -p 8080:80 --name web nginx。检查 docker ps 的 PORTS 列确认映射成功。
案例 D:Nginx 反向代理目标服务不可达导致 502
现象:Nginx 反向代理配置后,访问网站返回 502 Bad Gateway,但后端服务实际在运行:
# 客户端访问
$ curl -v http://10.0.0.5/
* Connected to 10.0.0.5 (10.0.0.5) port 80
> GET / HTTP/1.1
< HTTP/1.1 502 Bad Gateway
< Server: nginx/1.18.0
...
502 Bad Gateway
nginx/1.18.0
排查过程:检查 Nginx 配置和后端服务状态:
# 查看 Nginx 配置中的 upstream
$ grep -A5 "upstream" /etc/nginx/conf.d/app.conf
upstream backend {
server 127.0.0.1:8080;
}
# 检查后端服务是否在监听
$ ss -tlnp | grep 8080
# 无输出——后端服务未启动
# 启动后端服务后验证
$ systemctl start myapp
$ ss -tlnp | grep 8080
LISTEN 0 128 0.0.0.0:8080 0.0.0.0:* users:(("myapp",pid=1234,fd=6))
根因:Nginx 配置的 upstream 指向 127.0.0.1:8080,但后端服务未启动。Nginx 无法连接到上游服务器,返回 502 错误。
修复:启动后端服务并设置开机自启。同时优化 Nginx 配置添加健康检查:
upstream backend {
server 127.0.0.1:8080 max_fails=3 fail_timeout=30s;
keepalive 32;
}
server {
location / {
proxy_pass http://backend;
proxy_next_upstream error timeout http_502 http_503;
proxy_connect_timeout 5s;
}
}
预防:使用 systemctl enable 服务名 设置服务开机自启。配置 Nginx 健康检查和故障转移机制。
案例 E:SSH 端口被防火墙 DROP 导致连接超时(与 Connection refused 的区别)
现象:运维修改防火墙规则后,SSH 连接从正常变为超时:
# 修改前正常
$ ssh -v 10.0.0.11 2>&1 | head -5
OpenSSH_8.9p1 Ubuntu-3
debug1: Connecting to 10.0.0.11 port 22.
debug1: Connection established.
# 修改后超时
$ ssh -v 10.0.0.11 2>&1 | head -5
OpenSSH_8.9p1 Ubuntu-3
debug1: Connecting to 10.0.0.11 port 22.
debug1: connect to 10.0.0.11 port 22: Connection timed out
排查过程:Connection timed out 与 Connection refused 有本质区别:
# Connection refused = 目标端口有服务但拒绝(或无服务)
$ nc -zv 10.0.0.11 22
nc: connect to 10.0.0.11 port 22 (tcp) failed: Connection refused
# Connection timed out = 连接被静默丢弃
$ nc -zv 10.0.0.11 22
nc: connect to 10.0.0.11 port 22 (tcp) failed: Connection timed out
# 检查防火墙是否有 DROP 规则
$ sudo iptables -L -n | grep 22
DROP tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:22
根因:防火墙使用了 DROP(静默丢弃)而非 REJECT(主动拒绝)策略。客户端发送的 SYN 包被静默丢弃,没有收到任何响应,导致连接超时。
修复:
# 删除 DROP 规则
$ sudo iptables -D INPUT 1
# 或添加 ACCEPT 规则(优先级更高)
$ sudo iptables -I INPUT -p tcp --dport 22 -j ACCEPT
# 持久化规则
$ sudo iptables-save > /etc/iptables/rules.v4
预防:理解 DROP 与 REJECT 的区别。调试时使用 REJECT 快速获得反馈,生产环境根据安全需求选择。修改防火墙规则前先备份当前规则。
案例 F:容器间 Docker 网络隔离导致服务互访失败
现象:两个 Docker 容器在同一宿主机上,但 A 容器无法连接 B 容器:
# 在 A 容器中
$ docker exec container-a curl http://container-b:8080
curl: (7) Failed to connect to container-b port 8080: Connection refused
# B 容器内服务正常
$ docker exec container-b curl localhost:8080
{"status":"ok"}
排查过程:
# 查看容器网络
$ docker network ls
NETWORK ID NAME DRIVER SCOPE
abc123... bridge bridge local
def456... host host local
# 检查容器是否在同一网络
$ docker inspect container-a --format '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'
172.17.0.2
$ docker inspect container-b --format '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'
172.17.0.3
# 尝试用 IP 连接
$ docker exec container-a curl http://172.17.0.3:8080
{"status":"ok"}
# 用容器名连接失败的原因:未加入同一用户定义网络
根因:两个容器虽然都在默认的 bridge 网络中,但默认 bridge 网络不支持容器名解析。只有用户定义的自定义网络才支持 DNS 服务发现。
修复:
# 创建自定义网络
$ docker network create app-net
# 将两个容器加入同一网络
$ docker network connect app-net container-a
$ docker network connect app-net container-b
# 现在可以用容器名互访
$ docker exec container-a curl http://container-b:8080
{"status":"ok"}
预防:Docker Compose 中的 services 默认会创建共享网络。手动部署容器时,显式创建自定义网络并加入所有需要互访的容器。
预防措施
- 部署服务后立即验证监听地址和端口:
ss -tlnp | grep 端口号 - 使用配置管理工具(Ansible、Terraform)统一管理防火墙规则,避免手动配置遗漏
- 编写服务健康检查脚本,定期检测关键端口的可达性
- Docker 容器部署后在
docker-compose.yml中明确声明端口映射 - systemd 服务的 unit 文件里显式指定监听地址(如 ExecStart 参数带
0.0.0.0),防止默认 127.0.0.1 造成线上事故 - 防火墙规则变更走评审流程:先在测试环境验证,变更后立即用
nc -zv做回归检查 - 定期导出审查防火墙规则集(
nft list ruleset),清理已不再使用的放行规则
进阶排查技巧
使用 tcpdump 抓包分析连接失败原因
当常规工具无法定位问题时,抓包可以提供最底层的证据:
# 在服务端抓包(捕获目标端口的流量)
sudo tcpdump -i any port 8080 -n -v
# 输出示例:
# 10.0.0.10.54321 > 10.0.0.5.8080: Flags [S], ... # 客户端发送 SYN
# 10.0.0.5.8080 > 10.0.0.10.54321: Flags [R.], ... # 服务端返回 RST(Connection refused)
# 或:无任何响应(被 DROP)
# 在客户端抓包
sudo tcpdump -i any host 10.0.0.5 port 8080 -n
使用 ss 分析连接状态
# 查看所有 TCP 连接状态
ss -tan
# 统计各状态连接数
ss -tan | awk '{print $1}' | sort | uniq -c | sort -rn
# 查看特定端口的连接
ss -tanp | grep :8080
# 查看 SYN_RECV 状态(可能遭受 SYN flood 攻击)
ss -tan state syn-recv | wc -l
使用 nmap 扫描端口状态
# 扫描单个端口
nmap -p 8080 10.0.0.5
# 扫描端口范围
nmap -p 1-1024 10.0.0.5
# 扫描所有端口(耗时较长)
nmap -p- 10.0.0.5
# 输出解读:
# open = 服务在监听
# closed = 端口可达但无服务(收到 RST)
# filtered = 被防火墙过滤(无响应或 ICMP 不可达)
使用 curl 测试 HTTP 服务
# 测试 HTTP 连接
curl -v http://10.0.0.5:8080/
# 测试 HTTPS 连接(忽略证书错误)
curl -k -v https://10.0.0.5:8443/
# 测试 TCP 连接
curl -v --connect-timeout 5 telnet://10.0.0.5:8080
# 测试 DNS 解析
nslookup example.com
dig example.com
常见误区与陷阱
- 不要混淆 Connection refused 与 Connection timed out:前者是主动拒绝(RST),后者是静默丢弃(DROP)
- 不要忽略服务启动延迟:服务重启后需要几秒钟才能开始监听端口
- 不要在防火墙中使用过于宽泛的规则:如
iptables -A INPUT -j ACCEPT会开放所有端口 - 不要忽略 Docker 的网络隔离:容器间默认不支持 DNS 服务发现,需要使用自定义网络
- 不要在生产环境使用 telnet 调试:它会留下明文传输的凭证,使用 nc 或 ssh 替代
故障复现与验证清单
修复 connection refused 问题后,使用以下清单验证:
# 1. 验证服务监听状态
ss -tlnp | grep 端口号
# 2. 验证本机连接
curl http://127.0.0.1:端口号
# 3. 验证远程连接
nc -zv 目标IP 端口号
# 4. 验证防火墙规则
sudo nft list ruleset | grep 端口号
# 5. 验证服务日志无报错
journalctl -u 服务名 -n 10 --no-pager
最佳实践
- 分层排查:先确认服务端口是否监听 → 再检查防火墙 → 最后检查网络路由和 DNS,逐层排除
- 使用 REJECT 而非 DROP:在开发环境中,防火墙使用 REJECT 可以快速得到 Connection refused 反馈;生产环境根据安全需求选择
- 端口扫描先行:使用
nmap -p 1-65535 目标IP快速了解目标主机的端口开放情况,缩小排查范围 - 日志驱动排查:查看
journalctl -u 服务名和/var/log/syslog,通常能直接找到连接被拒的根本原因 - 记录端口分配表:维护一份文档记录所有服务的端口分配,避免端口冲突和配置遗漏
延伸阅读
- 4.5:网络故障排查 网络故障排查——tcpdump、ss、mtr
- 5.5:防火墙实战 防火墙实战——nftables/iptables 规则
- 2.11:SSH 深入 SSH 深入——端口转发绕过防火墙
- 4.5:网络故障排查 网络故障排查——TCP 连接建立过程(三次握手)
- Docker 容器网络模型——桥接、host、overlay,参考 3.1:Docker 容器入门 容器技术
- systemd 服务管理——unit 文件编写与依赖管理,参考 2.12:systemd 深入 systemd 深入
附录:网络排查命令速查表
| 任务 | 命令 | 说明 |
|---|---|---|
| 测试端口连通性 | nc -zv 目标IP 端口 | 返回 Connection refused 或成功 |
| 查看监听端口 | ss -tlnp | 列出所有 TCP 监听端口 |
| 查看防火墙规则 | sudo nft list ruleset | 显示 nftables 规则 |
| 查看路由表 | ip route | 检查网络路由 |
| 测试 DNS 解析 | nslookup 域名 | 检查 DNS 是否正常 |
| 跟踪路由 | traceroute 目标IP | 查看数据包路径 |
| 抓包分析 | sudo tcpdump -i any port 端口 | 捕获网络流量 |
| 查看网络接口 | ip addr show | 检查 IP 地址配置 |
附录:Docker 网络排查命令
# 查看容器网络配置
docker inspect 容器名 --format '{{json .NetworkSettings}}'
# 查看容器 IP 地址
docker inspect 容器名 --format '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'
# 进入容器网络命名空间
docker exec -it 容器名 sh
# 在容器内测试连通性
docker exec 容器名 ping 目标IP
docker exec 容器名 curl http://目标:端口
# 查看 Docker 网络列表
docker network ls
# 查看网络详情
docker network inspect bridge
附录:防火墙规则管理速查
# nftables(现代系统推荐)
sudo nft list ruleset # 查看所有规则
sudo nft add rule inet filter input tcp dport 80 accept # 放行 80 端口
sudo nft delete rule inet filter handle 3 # 删除规则
# UFW(Ubuntu)
sudo ufw status verbose # 查看状态
sudo ufw allow 80/tcp # 放行 80 端口
sudo ufw delete allow 80/tcp # 删除规则
# firewalld(CentOS/RHEL)
sudo firewall-cmd --list-all # 查看所有规则
sudo firewall-cmd --permanent --add-port=80/tcp # 放行 80 端口
sudo firewall-cmd --reload # 重新加载
# iptables(传统)
sudo iptables -L -n -v # 查看规则
sudo iptables -A INPUT -p tcp --dport 80 -j ACCEPT # 放行 80 端口
sudo iptables -D INPUT 1 # 删除第一条规则
案例 G:IPv6 与 IPv4 绑定地址不匹配导致连接失败
现象:Nginx 配置监听 [::]:80(IPv6),但客户端通过 IPv4 地址连接时被拒绝:
# 客户端连接
$ curl -v http://10.0.0.5:80
curl: (7) Failed to connect to 10.0.0.5 port 80: Connection refused
# 服务端检查
$ ss -tlnp | grep :80
LISTEN 0 128 [::]:80 [::]:* users:(("nginx",pid=1234,fd=6))
根因:Nginx 只绑定了 IPv6 地址 [::],没有绑定 IPv4 地址 0.0.0.0。在启用了 IPv4 的系统上,客户端通过 IPv4 连接时无法到达仅监听 IPv6 的服务。
修复:修改 Nginx 配置,同时监听 IPv4 和 IPv6:
# 修改 Nginx 配置
server {
listen 80; # 监听所有 IPv4 地址
listen [::]:80; # 同时监听 IPv6 地址
...
}
# 重启 Nginx
sudo nginx -t && sudo systemctl restart nginx
# 验证
$ ss -tlnp | grep :80
LISTEN 0 128 0.0.0.0:80 0.0.0.0:* users:(("nginx",...))
LISTEN 0 128 [::]:80 [::]:* users:(("nginx",...))
预防:服务配置中明确指定同时监听 IPv4 和 IPv6,或根据网络环境选择合适的监听地址。部署后用 ss -tlnp 确认绑定地址。
案例 H:systemd socket activation 导致服务端口未监听
现象:服务通过 systemd 管理,systemctl status 显示服务运行中,但端口无法连接:
# 服务状态正常
$ systemctl status myapp
● myapp.service - My Application
Active: active (running) since ...
# 但端口未监听
$ ss -tlnp | grep :8080
# 无输出
# 检查 systemd socket 配置
$ cat /etc/systemd/system/myapp.socket
[Socket]
ListenStream=8080
根因:服务使用了 systemd socket activation,但服务进程没有正确接收 socket 文件描述符。systemd 会创建 socket 并监听端口,但服务进程需要通过 sd_listen_fds() API 接收。如果服务代码未实现此机制,socket 会处于 listening 状态但无法处理连接。
修复:检查服务是否正确实现了 socket activation,或改用直接端口绑定:
# 方案一:禁用 socket activation,让服务自行监听
sudo systemctl stop myapp.socket myapp
sudo systemctl disable myapp.socket
sudo systemctl start myapp
# 方案二:在服务代码中实现 socket activation
# 需要调用 sd_listen_fds() 接收 socket fd
预防:使用 systemd socket activation 前确认服务代码支持该机制。部署后用 ss -tlnp 确认端口状态。