Ошибка ConnectionRefusedError: [WinError 10061] Подключение не установлено, т.к. конечный компьютер отверг запрос на подключение возникает в Python, когда сокет-клиент пытается достучаться до адреса и порта, которые никто не слушает. Это не сбой интерпретатора и не проблема синтаксиса: операционная система целевой машины (или вашей собственной, если речь про localhost) получила TCP-пакет и ответила отказом, потому что на указанном порту нет работающего сервиса.
Чаще всего с этой ошибкой сталкиваются при подключении к базам данных (MySQL, PostgreSQL, Redis, MongoDB), при запуске локального веб-сервера и клиента в разных скриптах, при обращении к API через requests или при работе с сокетами напрямую. Ниже разберём, как диагностировать причину и устранить её, независимо от того, какая именно библиотека выбросила исключение.
Что означает ошибка 10061 на техническом уровне
Когда вы вызываете socket.connect() или любую обёртку вроде requests.get(), операционная система отправляет SYN-пакет на целевой адрес и порт. Если на той стороне порт закрыт — то есть ни один процесс не выполнил bind() и listen() для него, — стек TCP/IP отвечает пакетом RST (reset). Windows в этот момент возвращает приложению код WSAECONNREFUSED (10061), а Python превращает его в исключение ConnectionRefusedError.
Важно отличать этот код от таймаута. Отказ в подключении означает, что хост достижим, но порт закрыт, тогда как таймаут говорит о том, что пакеты вообще не доходят (файрвол в режиме «молчаливого отброса», недоступная сеть, неверный IP). Это ключевая диагностическая развилка: при ConnectionRefusedError сеть между машинами, скорее всего, работает, и проблему нужно искать в серверном приложении или привязке порта.
Основные причины отказа в подключении
Прежде чем что-то исправлять, стоит понять, какой из типовых сценариев ваш. Ниже перечислены причины, которые покрывают подавляющее большинство случаев.
- 🔌 Серверное приложение не запущено. Самая частая причина: вы запускаете клиентский скрипт, а сервер (ваш собственный, СУБД или локальный API) просто не стартовал или упал с ошибкой при запуске.
- 🔢 Неверный порт или хост. Клиент стучится на порт
8080, а сервер слушает8000. Или в коде указанlocalhost, а сервис работает на другой машине. - 🧱 Привязка к другому интерфейсу. Сервер слушает только
127.0.0.1, а вы подключаетесь по внешнему IP машины — или наоборот, сервер привязан к конкретному сетевому интерфейсу. - 🛡️ Файрвол или антивирус. Брандмауэр Windows или сторонний антивирус блокирует входящие соединения на порт сервера.
- 🐍 Сервер упал из-за исключения. Ваш серверный скрипт стартовал, но завершился с traceback до того, как клиент успел подключиться.
- 🌐 Прокси или VPN. Переменные окружения
HTTP_PROXY/HTTPS_PROXYперенаправляют запросы requests на прокси, который недоступен.
Шаг 1. Проверяем, слушается ли порт
Первое действие — убедиться, что на целевой машине вообще есть процесс, слушающий нужный порт. В Windows откройте командную строку и выполните:
netstat -ano | findstr :8000
Замените 8000 на ваш порт. Если в выводе есть строка со состоянием LISTENING — порт занят серверным процессом, значит проблема в адресе подключения или файрволе. Пустой вывод означает, что сервер не запущен или слушает другой порт, и начинать нужно именно с него. В Linux и macOS аналогичная проверка делается через ss -tlnp | grep 8000 или lsof -i :8000.
Дополнительно можно проверить доступность порта прямо из Python, не запуская основной клиент:
import socket
s = socket.socket()
s.settimeout(3)
try:
s.connect(("127.0.0.1", 8000))
print("Порт открыт")
except ConnectionRefusedError:
print("Порт закрыт — сервер не слушает")
finally:
s.close()
Шаг 2. Сверяем адрес, порт и порядок запуска
Когда сервер — ваш собственный код, проверьте три вещи. Во-первых, порядок запуска: серверный скрипт должен быть запущен до клиентского, иначе клиенту некуда подключаться. Во-вторых, совпадение порта: сравните аргумент bind() на сервере и connect() на клиенте — опечатка в одной цифре даёт ровно эту ошибку.
В-третьих, адрес привязки. Если сервер делает server.bind(("127.0.0.1", 8000)), он принимает соединения только с той же машины. Подключение с другого компьютера в сети будет отклонено. Для приёма внешних подключений привязывайтесь к 0.0.0.0 — но делайте это осознанно, понимая, что сервис станет доступен из сети. Во фреймворках то же самое: у Flask это параметр app.run(host="0.0.0.0"), у Uvicorn — флаг --host 0.0.0.0.
☑️ Диагностика ConnectionRefusedError
Шаг 3. Файрвол и конфликты портов
Если сервер слушает порт, но подключение с другой машины отклоняется, вероятный виновник — брандмауэр Windows. По умолчанию он блокирует входящие соединения для неизвестных программ. Создайте правило: Панель управления → Брандмауэр Защитника Windows → Дополнительные параметры → Правила для входящих подключений → Создать правило → Для порта, укажите TCP и номер вашего порта. Точные названия пунктов могут немного отличаться в зависимости от версии Windows.
⚠️ Внимание: не отключайте брандмауэр целиком «на постоянку» ради отладки. Для проверки можно отключить его временно, но после диагностики верните защиту и настройте точечное правило только для нужного порта.
Отдельный сценарий — ошибка при запуске сервера вида OSError: [WinError 10048] («порт уже занят»). Тогда сервер падает, а клиент получает 10061. Найдите процесс, занявший порт, через netstat -ano | findstr :8000 — последний столбец покажет PID, который можно сопоставить с процессом в Диспетчере задач и завершить его.
Особые случаи: базы данных и requests
При подключении к СУБД ошибка 10061 почти всегда означает, что служба базы данных остановлена. Для MySQL на Windows проверьте службу в оснастке services.msc (служба обычно называется MySQL или MySQL80 в зависимости от версии установки), для PostgreSQL — службу вида postgresql-x64-XX. Если служба остановлена, запустите её и повторите подключение. Также убедитесь, что в строке подключения указан правильный порт: он задаётся при установке СУБД и может отличаться от привычного.
С библиотекой requests ситуация хитрее: та же ошибка может возникать из-за прокси. Проверьте переменные окружения:
echo %HTTP_PROXY%
echo %HTTPS_PROXY%
Если там прописан недоступный прокси-сервер, requests попытается подключиться к нему и получит отказ. Решение — очистить переменные или передать в запрос proxies={"http": None, "https": None}. Ещё одна возможная причина — антивирус с функцией проверки HTTPS-трафика, который перехватывает соединения; проверьте, исчезает ли ошибка при временном отключении этой функции.
Почему ошибка возникает «иногда», а не всегда
Нестабильный ConnectionRefusedError обычно означает, что серверный процесс периодически падает и перезапускается (например, под supervisor или в Docker с restart-политикой), либо клиент подключается раньше, чем сервер успевает выполнить bind() и listen(). В таких случаях помогает повтор попыток с задержкой: оберните connect в цикл с time.sleep(1) и разумным лимитом попыток.
Шаблон устойчивого клиента с повторными попытками
Если клиент и сервер запускаются почти одновременно (например, в Docker Compose или в автотестах), гонка запуска неизбежна. Практичное решение — короткий цикл повторных попыток:
import socket
import time
def connect_with_retry(host, port, attempts=10, delay=1):
for i in range(attempts):
try:
s = socket.create_connection((host, port), timeout=3)
return s
except ConnectionRefusedError:
print(f"Попытка {i + 1}: сервер ещё не готов")
time.sleep(delay)
raise RuntimeError("Сервер не отвечает после всех попыток")
⚠️ Внимание: не ставьте бесконечный цикл повторов без ограничения — если сервер упал окончательно, скрипт зависнет навсегда. Всегда задавайте лимит попыток и логируйте каждую неудачу.
Сводная таблица диагностики
| Симптом | Вероятная причина | Что делать |
|---|---|---|
| netstat не показывает порт | Сервер не запущен или упал | Запустить сервер, проверить его логи |
| Порт слушается на 127.0.0.1 | Привязка только к loopback | Привязать сервер к 0.0.0.0 |
| Локально работает, по сети — нет | Файрвол блокирует порт | Создать разрешающее правило для порта |
| Ошибка только в requests | Прокси в переменных окружения | Проверить HTTP_PROXY/HTTPS_PROXY |
| Ошибка при подключении к СУБД | Служба базы данных остановлена | Запустить службу через services.msc |
Часто задаваемые вопросы
Ошибка 10061 — это проблема в моём коде на Python?
Не обязательно. Исключение лишь сообщает, что целевой порт никто не слушает. Сам код клиента может быть полностью корректным — сначала проверьте, запущен ли сервер и на том ли порту он работает.
Чем отличается ConnectionRefusedError от TimeoutError?
Отказ (10061) означает, что хост достижим, но порт закрыт — ответ пришёл мгновенно. Таймаут означает, что ответа нет вообще: пакеты теряются по пути, их молча отбрасывает файрвол или хост недоступен. Это разные сценарии диагностики.
Почему подключение к localhost тоже отклоняется?
Потому что даже на локальной машине порт должен кем-то слушаться. Если серверный скрипт не запущен, упал с ошибкой или слушает другой порт, подключение к 127.0.0.1 будет отклонено точно так же, как к удалённому хосту.
Может ли антивирус вызывать эту ошибку?
Да. Антивирусы с сетевым экраном или проверкой защищённого трафика могут блокировать соединения, и приложение получает отказ. Проверьте, исчезает ли ошибка при временном отключении сетевой защиты, и если да — добавьте ваш порт или процесс в исключения.
Что делать, если сервер и клиент запускаются одновременно в Docker?
Используйте механизм ожидания готовности: healthcheck с depends_on в Docker Compose либо цикл повторных попыток подключения в коде клиента. Полагаться на порядок запуска контейнеров нельзя — сервису нужно время на инициализацию.