ConnectionRefusedError 连接被拒速查:[Errno 111] 六大病因定位
更新时间:2026-10-24。FAQ 速查页,面向 Python 客户端一发起连接就抛
ConnectionRefusedError: [Errno 111] Connection refused的同学。ConnectionRefusedError 连接被拒是 Python 网络编程里最常见的报错:requests、aiohttp、redis、pymysql、socket 裸调用,底层全是同一件事——客户端发出了 TCP 的 SYN,对端内核回了 RST,意思是「这个地址上没有进程在听这个端口」。注意它和「超时」是两码事:超时是包没人理(被防火墙静默丢弃),拒绝是有人明确回话「没有」。所以 ConnectionRefused 的排查范围天然小:要么服务没起、要么端口不对、要么绑定地址不对、要么被中间设备主动重置。本篇给六个病因的逐条验证与处置口径。
本文要回答的问题
- [Errno 111] 和超时、DNS 失败怎么区分?
- 本机能连、跨机就连不上,问题出在哪?
- 服务明明起来了,为什么还是拒绝?
- http_proxy 环境变量怎么会把内网请求搞成拒绝?
- 怎么 30 秒判断锅在客户端还是服务端?
一、报错原文与典型现象
真身(工程路径以 /proj/demo 占位):
$ python3 /proj/demo/client.py
Traceback (most recent call last):
File "/proj/demo/client.py", line 6, in <module>
r = requests.get("http://backend:8080/health")
ConnectionRefusedError: [Errno 111] Connection refused
# requests 场景外层会被包成 requests.exceptions.ConnectionError,
# 但根因行一定是 Max retries exceeded ... Caused by NewConnectionError ... [Errno 111]$ python3 -c "import socket; socket.create_connection(('db.internal', 5432), timeout=3)"
ConnectionRefusedError: [Errno 111] Connection refused# 对比:超时长这样(不是一回事)
TimeoutError: [Errno 110] Connection timed out
# DNS 失败长这样
socket.gaierror: [Errno -2] Name or service not known先认清报错的网络语义:Errno 111 = 对端主机回了 RST 包。这台机器是「活着且能收到你的包」的——否则就是超时了。所以排查第一问永远是「这台目标机上,到底有没有进程在监听这个端口」。
二、病因处置速查表
| 报错现象 / 场景 | 可能原因 | 快速验证 | 处置 |
|---|---|---|---|
| 起服务前就调,必现拒绝 | 服务进程根本没起或起失败 | ss -tlnp 看端口是否 LISTEN | 先起服务,看启动日志是否报错 |
| 端口写错一位 | 客户端连的端口与服务监听不一致 | 对比两端配置的端口数字 | 统一端口,配置走同一处环境变量 |
| 本机 curl 通、跨机不通 | 服务绑定了回环地址(127 开头那个环回 IP) | ss -tlnp 看监听地址是不是只有环回 | 绑定改为 「四个零的通配地址」 或具体网卡地址 |
| 绑对了还是跨机不通 | 防火墙/安全组主动 reject | nc -vz <目标> <端口> 看是 refused 还是超时 | 放通端口,reject 规则改 drop 会变超时 |
| 只有 Python 脚本被拒、curl 正常 | http_proxy 等代理环境变量误伤 | env | grep -i proxy 检查 | 清掉代理变量或配 no_proxy |
| localhost 被拒、换 <回环地址> 就好(或反过来) | IPv6/IPv4 解析不一致 | getent hosts localhost 看解析到哪个族 | 客户端显式指定地址族或写死地址 |
三、排查决策图
这张图是连接被拒的标准动线:红是报错入口与定罪根因,蓝是排查动作,绿是验证通过,橙是修复动作,紫是防复发沉淀。
读这张图抓住主线:连接被拒的排查是「先服务端后客户端」——先在目标机上用 ss -tlnp 确认监听事实(起没起、什么地址、什么端口),再回到客户端查代理与地址族。顺序反过来会在客户端瞎试半天,而服务端压根没起来。
四、逐条排查展开
第一病因:服务根本没起或起失败。 最朴素也最高频:脚本写好了,服务进程其实没跑,或者启动即崩溃(配置错、端口被占)。验证只要一条命令(在服务端机器上执行):
$ ss -tlnp | grep 8080
# 没有任何输出 = 没进程在听,Errno 111 天经地义处置:先把服务拉起来,看启动日志——端口被占会报 Address already in use,那是另一个报错、另一种处置。养成习惯:报连接被拒时第一反应不是改客户端,而是上服务端看监听。
第二病因:端口写错。 服务听 8080,客户端连 8000——对端内核如实回答「这个端口没人」,Errno 111。微服务与容器环境尤其高发:compose 里 ports: "8080:80" 是宿主机 8080 映射容器 80,从别的容器访问该走服务名加容器端口 80,从宿主机访问该走 8080,三套口径混在一起就错。处置:端口只允许有一个来源——写进同一份配置/环境变量,两端都从那里读。
第三病因:绑定回环地址,跨机访问不通。 经典剧本:开发时 app.run(host="<回环地址>") 或默认绑定环回 IP,本机 curl localhost:8080 一切正常,部署后跨机访问全部 Errno 111。ss -tlnp 里监听地址那列是环回 IP 而不是 「四个零的通配地址」 就是实锤。原理:绑定环回地址意味着内核只接受目标是环回网卡的包,从物理网卡进来的 SYN 找不到监听者,直接回 RST。处置:服务端绑定 「四个零的通配地址」(监听所有网卡),生产上更讲究就绑定具体的内网网卡地址。注意这是「服务端的 bind 参数」问题,跟客户端怎么写地址无关。
第四病因:防火墙/安全组主动 reject。 iptables 的 -j REJECT 规则或云安全组的隐式拒绝,会让对端回 RST,表现和「没人监听」一模一样。区分方法:
$ nc -vz <目标主机> 8080
# refused = 包到了主机被拒(查监听/防火墙 reject)
# 卡住到超时 = 包被静默丢弃(查防火墙 drop/安全组/路由)处置:放通端口;顺带提醒,云主机有「安全组 + 系统防火墙」两层,只开一层没用。
第五病因:代理环境变量误伤。 只有 Python 脚本失败、curl 却通的诡异场景,九成是 http_proxy/https_proxy 环境变量。requests/urllib3 默认尊重它们,把本该直连内网的请求发去代理服务器,代理不转发内网地址就回拒绝。验证:env | grep -i proxy。处置:脚本内显式 session.trust_env = False,或给内网域名配 no_proxy。CI/容器里尤其要查,基础镜像常自带代理变量。
第六病因:IPv6 与 IPv4 混用。 localhost 在某些机器上解析到 ::1(IPv6 回环),而服务只绑了 IPv4 的回环地址,于是连 ::1 被拒。反之亦然。验证:getent hosts localhost 看解析结果;客户端换成显式地址一试便知。处置:客户端显式指定地址族,或服务端双栈监听;写代码时别对 localhost 的解析结果做假设。
30 秒定位心法。 一把梭口诀:服务端 ss -tlnp 看监听(起没起、绑哪、什么端口)→ 客户端 nc -vz 看是 refused 还是超时(分清「主机活着拒」与「路不通」)→ env | grep -i proxy 排除代理。三步走完,六个病因里至少五个已经现形。
五、规范做法与预防
- 服务上线即配健康检查:编排系统探活端口,没起来就告警,而不是等调用方报 Errno 111。
- 端口与地址走环境变量单源:客户端服务端读同一份配置,杜绝两端各自硬编码。
- 绑定地址显式声明:代码里写死 「四个零的通配地址」 或具体网卡,不依赖框架默认值(Flask/FastAPI 默认绑定行为各不相同)。
- 代理变量进部署清单:容器镜像与 CI 环境显式清理或配置
no_proxy,内网域名全覆盖。 - 故障口诀进 oncall 手册:「先 ss 看监听,再 nc 分拒/丢,后 env 查代理」。
这一篇在知识体系里的位置
本篇是 Python 排错速查簇的网络连接篇,定位「对端明确说没人听」的 Errno 111:包被静默丢弃的超时场景看 Python 脚本卡死不退出速查;缺模块连不上依赖服务前的安装问题看 pip install 失败速查;依赖冲突装不上看 pip 依赖冲突装不上速查;本地文件撞标准库引发的诡异报错看 模块重名遮蔽标准库速查;路径类报错看 文件路径不存在速查。「先服务端看监听、再客户端查代理」这条动线对所有 TCP 客户端通用。
一句话总结
ConnectionRefusedError([Errno 111])的网络语义是「对端主机活着、但这个端口没人听」,排查铁律是先服务端后客户端:服务端 ss -tlnp 确认监听事实——没起就起服务并看日志、端口错就两端配置统一、绑定回环地址就改绑 「四个零的通配地址」;再客户端 nc -vz 区分 refused 与超时(前者查防火墙 reject,后者查安全组 drop),env | grep -i proxy 排除代理环境变量误伤,getent hosts localhost 排除 IPv6/IPv4 混用;防复发主线一条:端口与代理配置单源化、服务上线即探活,让「没人听」在服务起来那一刻就暴露。