W weiserv
← 返回博客

Connection refused 与 timed out:Python 连接报错的两种真相与排查

报错 / 症状

调接口或连数据库时,常遇到连接类报错。最典型的一句:

requests.exceptions.ConnectionError: ... Failed to establish a new connection: [Errno 111] Connection refused

但在本机实测时,我遇到的是另一种表现:

TimeoutError: timed out

同样是"连不上",为什么有时报 refused、有时报 timed out?二者排查方向完全不同,本文用 Python 3.13 / Windows 11 实跑讲清。

触发环境

  • OS:Windows 11
  • Python:3.13.14(CPython)
  • 仅用标准库 socket / urllib,无第三方依赖

根因分析

TCP 建连靠三次握手。目标端口的响应决定你看到哪种报错:

  • RST(复位) → 主机可达,但那个端口没有进程在监听。操作系统直接回 RST,Python 抛 ConnectionRefusedError(Linux 下为 [Errno 111] Connection refused,Windows 下为 [WinError 10061])。
  • DROP(丢弃) → 主机不可达,或中间防火墙静默丢弃了包(既不回 RST 也不回 ICMP)。客户端迟迟等不到回应,最终 TimeoutError: timed out

一句话:refused = 找得到门但没人在;timed out = 门都没找着(或被墙拦了)。

解决方法

A. 标准环境遇到 ConnectionRefusedError(最常见)排查清单:

  1. 目标服务真的启动了吗ps / netstat -ano | findstr :端口 确认进程在监听;
  2. 端口写对了吗?本地起 8000 却连 8080 是高频翻车点;
  3. 主机/IP 对吗?容器里连 localhost 往往不互通,要用服务名或 host.docker.internal
  4. Docker 端口映射 -p 8000:8000 漏了?宿主机连不进容器;
  5. 云服务器安全组 / 防火墙是否放行该端口。

B. 本机实测表现为 timed out 时: 多半是目标不可达或路径上有 DROP 策略(含本机回环被网络策略拦截)。先 ping / telnet 主机 端口 确认网络层可达,再回头查防火墙。

终端输出(实证)

本机连接一个未监听的回环端口,真实输出是超时(本环境对未放行端口做 DROP,故不回 RST):

=== Python 3.13.14 | Windows 11 实测 ===

【场景1】socket.create_connection(('127.0.0.1', 1)) —— 端口无服务
    报错: TimeoutError
    信息: timed out

【场景2】urllib.request.urlopen('http://127.0.0.1:1') —— HTTP 请求打空端口
    报错: URLError
    信息: timed out

本机取到的系统错误码常量(印证 refused 与 timeout 是两个不同 errno):

=== socket errno 常量(本机 Windows 11 / Python 3.13.14 实测)===
  errno.ECONNREFUSED = 10061
  errno.ETIMEDOUT   = 10060

⚠️ 说明(非本机实跑,依据 OS 语义):在标准 Windows / Linux 上,连接一个确实无监听的端口,同一段 socket.create_connection(('127.0.0.1', 1)) 会得到:
ConnectionRefusedError: [WinError 10061] No connection could be made because the target machine actively refused it
requests 会把它包成 requests.exceptions.ConnectionError: ... [Errno 111] Connection refused。本沙盒环境因网络策略把回环包 DROP,故只观察到 timed out——这正是本文要区分的两种真相。

预防措施

  • 连接前确认服务已起、端口/主机无误,优先用健康检查(如 /healthz);
  • 客户端加连接超时与重试(指数退避),别让单次失败卡死;
  • 区分"主动拒绝"与"网络不可达",看错误类型而非笼统说"连不上";
  • 容器/云环境把端口映射与安全组当成一等公民来核对。

本文环境:Django 4.2 / Python 3.11 / Ubuntu 22.04

广告位占位 · post-inline