HOWTO · Python

Cómo solucionar ConnectionRefusedError: [Errno 111] Connection Refused

En este artículo aprenderemos qué es ConnectionRefusedError; básicamente, este error indica que el cliente no puede conectarse al puerto del sistema que ejecuta el script del servidor.

En esta página

ConnectionRefusedError: [Errno 111] Connection refused significa que un intento de conexión TCP llegó a un destino que la rechazó, normalmente porque ningún servicio acepta conexiones en esa dirección y puerto. Un ping correcto solo muestra que un host puede responder a sondeos de red; no confirma que un servicio TCP concreto esté escuchando.

Por qué Python genera ConnectionRefusedError: [Errno 111] Connection refused

El sistema operativo comunica un rechazo a Python cuando el endpoint TCP solicitado rechaza la conexión. Comprueba primero si el proceso del servidor se está ejecutando y está escuchando. Después compara la dirección y el puerto exactos que usa el cliente con la dirección y el puerto de escucha del servidor. Un servidor vinculado solo a 127.0.0.1 acepta conexiones desde su propia máquina, no desde otro equipo. Un cortafuegos también puede rechazar una conexión.

Esto difiere de un fallo de búsqueda de nombre de host, que se produce antes de que pueda intentarse una conexión TCP. Un tiempo de espera agotado o un fallo de enrutamiento suele significar que el cliente no recibió una respuesta de rechazo. Una dirección incorrecta puede dar lugar a resultados diferentes según la red; por sí sola no garantiza este error.

Diagnostica y corrige una conexión de socket de Python rechazada

Comprueba primero el servidor y después verifica la dirección de destino y el puerto en ambos lados. Confirma la interfaz en la que escucha el servidor: usa loopback para un cliente en la misma máquina, o la interfaz de red prevista para otro host. Vincular a 0.0.0.0 escucha en todas las interfaces IPv4 y puede exponer el servicio a otras máquinas, así que elige la interfaz más restringida que se ajuste a tu configuración. Revisa las reglas del cortafuegos si el servicio está escuchando en el endpoint esperado.

Este ejemplo autónomo inicia un servidor local en un hilo en segundo plano, conecta un cliente al mismo puerto de loopback y transfiere una cadena de bytes. El sistema operativo asigna el puerto, de modo que ambos sockets usan el mismo endpoint sin necesidad de un archivo aparte ni un comando de shell. El cliente lee hasta recibir la longitud de respuesta conocida; si el servidor se cierra antes, informa de que la respuesta estaba incompleta en lugar de presentar datos parciales como un mensaje completo.

import socket
import threading

with socket.socket() as server:
    server.bind(("127.0.0.1", 0))
    server.listen()
    port = server.getsockname()[1]

    def serve():
        connection, _ = server.accept()
        with connection:
            connection.recv(1024)
            connection.sendall(b"hello from server")

    worker = threading.Thread(target=serve)
    worker.start()

    with socket.create_connection(("127.0.0.1", port)) as client:
        client.sendall(b"hello from client")
        response_length = len(b"hello from server")
        response = bytearray()
        complete = True
        while len(response) < response_length:
            chunk = client.recv(response_length - len(response))
            if not chunk:
                complete = False
                break
            response.extend(chunk)
        if complete:
            print(response.decode())
        else:
            print("Server closed before the full response arrived")

    worker.join()

En el caso de dos programas separados, inicia el servidor antes que el cliente y mantén el proceso del servidor en ejecución mientras espera en accept(). Establece el mismo puerto en ambos programas y haz que el cliente se conecte a una dirección alcanzable desde su propia máquina. Usa 127.0.0.1 solo cuando ambos programas se ejecuten en el mismo host; para un cliente remoto, usa la dirección de interfaz alcanzable de la máquina del servidor y configura el acceso de forma deliberada. El ejemplo anterior es una demostración en un solo proceso del intercambio correcto; no configura un listener de acceso público.

Si lo único que falla es un nombre de host, investiga la resolución de nombres por separado. socket.gethostbyname("example.com") busca una dirección IPv4; no inicia un servicio ni repara una conexión rechazada. Una vez que el nombre de host se resuelva, verifica que la dirección devuelta sea aquella en la que escucha el servidor y que el cliente use el puerto del servidor.

Conclusión

ConnectionRefusedError apunta a una conexión TCP rechazada. Comprueba que el servidor se esté ejecutando, que escuche en la interfaz prevista y que use la misma dirección y puerto a los que se dirige el cliente. Después revisa las reglas del cortafuegos; trata la resolución de nombres de host como un diagnóstico aparte.