Подключение не установлено, т.к. конечный компьютер отверг запрос на подключение: решаем ошибку в Python

Ошибка 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 на прокси, который недоступен.
📊 Где вы столкнулись с ошибкой ConnectionRefusedError?
При подключении к базе данных
При запуске клиент-серверных сокетов
При запросе через requests/urllib
При работе с Flask/Django/FastAPI

Шаг 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

Выполнено: 0 / 5

Шаг 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 либо цикл повторных попыток подключения в коде клиента. Полагаться на порядок запуска контейнеров нельзя — сервису нужно время на инициализацию.