HOWTO · Java

Java.Net.SocketTimeoutException: conexión agotada

En el artículo de hoy, hablaremos de java.net.SocketTimeoutException: se agotó el tiempo de espera de la conexión.

En esta página

java.net.SocketTimeoutException: Connection timed out significa que una operación de red no terminó dentro del tiempo permitido. Si ocurre al abrir una conexión de cliente, comprueba el host de destino, el puerto, la ruta, el servicio que escucha y el tiempo de espera configurado. El mensaje por sí solo no permite determinar cuál de esos elementos es responsable. Java o la pila de red subyacente pueden informar de un fallo de conexión con otro tipo de excepción o mensaje; por eso, además del texto, diagnostica la operación y el punto de conexión.

Qué significa java.net.SocketTimeoutException: Connection timed out

Un socket TCP identifica uno de los extremos de una conexión entre aplicaciones. Un cliente usa java.net.Socket para conectarse al java.net.ServerSocket de un servidor; el servidor acepta la conexión y recibe otro socket para comunicarse con ese cliente. El tiempo de espera limita cuánto aguarda la operación correspondiente. No repara una ruta ni permite acceder a un servicio que no está disponible.

Cuando un cliente llama a Socket.connect(SocketAddress, timeout), un tiempo de espera positivo, expresado en milisegundos, limita el intento de conexión. Si vence antes de que se complete la conexión, Java informa de un tiempo de espera agotado. Este ajuste es distinto del tiempo de espera de lectura del socket: la conexión puede establecerse y, aun así, una lectura posterior puede esperar demasiado por los datos.

Distinguir los tiempos de espera de conexión, lectura y aceptación

Elige el ajuste según la operación que esté esperando:

  • El tiempo de espera de conexión se aplica mientras un cliente establece una conexión saliente. Usa Socket.connect con un tiempo límite.
  • El tiempo de espera de lectura se aplica mientras un socket conectado espera datos entrantes. Se configura con Socket.setSoTimeout; no limita el establecimiento de la conexión.
  • El tiempo de espera de aceptación se aplica mientras un servidor espera a un cliente entrante. Se configura con ServerSocket.setSoTimeout; no limita la conexión saliente de un cliente.

Estos límites son independientes. Configura un límite para cada operación que deba tenerlo y elige valores adecuados para la latencia prevista de la red y del servicio. En estas API de socket, un tiempo de espera de cero significa esperar indefinidamente; no uses cero si necesitas un límite finito.

Causas frecuentes y cómo diagnosticarlas

Considera las causas posibles como hipótesis y compruébalas desde el equipo y la red donde se ejecuta el proceso Java:

  1. El tiempo de espera es demasiado corto. Compáralo con la latencia normal de conexión y el presupuesto de latencia de la aplicación. Auméntalo solo si esperas conexiones lentas que sí llegan a completarse; un límite mayor también hace que los fallos tarden más en manifestarse.
  2. El host o el puerto son incorrectos, o no hay ningún servicio escuchando. Confirma el nombre de host configurado, la dirección resuelta, el puerto y el servicio que escucha en el servidor. Comprueba que el servicio esté enlazado a una dirección accesible para el cliente.
  3. La ruta no está disponible. Comprueba la resolución DNS y el enrutamiento de red desde el entorno del cliente, incluida la configuración de red del contenedor o de la nube, si corresponde.
  4. Un cortafuegos o una política remota filtra el tráfico. Compara el acceso desde la misma red del cliente con el acceso desde una red autorizada y revisa las reglas de cortafuegos pertinentes o los registros del servicio.
  5. El servidor responde lentamente, está caído o tiene una carga excesiva. Comprueba su estado y los registros correspondientes al momento del intento de conexión. Un tiempo de espera agotado no demuestra que el servidor esté en buen estado ni que el tiempo de espera sea el único problema.

Una conexión rechazada y una conexión cuyo tiempo de espera se agota pueden indicar resultados de red distintos, aunque los mensajes varían según el sistema operativo y la ruta de red. Antes de cambiar los ajustes, registra el destino, la operación, el tiempo transcurrido, el límite configurado y los detalles originales de la excepción.

Configurar un tiempo de espera de conexión para un socket de cliente

Crea un Socket sin conectar y luego llama a Socket.connect con el destino y un tiempo de espera positivo y finito. Por ejemplo, las llamadas esenciales son Socket client = new Socket(); y client.connect(new InetSocketAddress(host, port), 5000);. Usa try-with-resources para que el socket se cierre al terminar y conserva el host y el puerto en los registros de diagnóstico.

Ejecuta este ejemplo autónomo para ver cómo un cliente se conecta a un servidor local en un puerto efímero. Usa un límite de conexión de un segundo; el servidor acepta un cliente y ambos sockets se cierran al finalizar el bloque. El código y las etiquetas o salidas visibles del ejemplo se mantienen en inglés.

import java.net.ServerSocket;
import java.net.Socket;

public class LocalConnect {
    public static void main(String[] args) throws Throwable {
        try (ServerSocket server = new ServerSocket(0)) {
            Thread worker = new Thread(() -> {
                try {
                    server.accept().close();
                } catch (Throwable ignored) {
                }
            });
            worker.start();
            try (Socket client = new Socket()) {
                client.connect(new java.net.InetSocketAddress("127.0.0.1", server.getLocalPort()), 1000);
                System.out.println("Connected to local server");
            }
            worker.join();
        }
    }
}
Connected to local server

La ejecución suministrada terminó con código de salida 0 e imprimió la línea que se muestra. La etiqueta visible permanece en inglés porque el bloque no se modifica. Este resultado local demuestra el patrón de uso de la API, pero no prueba que se pueda acceder a un host remoto de producción.

Configurar un tiempo de espera de aceptación en un socket de servidor

Llama a ServerSocket.setSoTimeout con un número positivo de milisegundos antes de llamar a accept(). Por ejemplo, server.setSoTimeout(10000); limita cuánto espera server.accept() a que llegue un cliente. Si no llega ninguno dentro de ese intervalo, la operación de aceptación que estaba esperando termina con SocketTimeoutException; el socket del servidor sigue disponible, así que un bucle del servidor puede capturar el tiempo de espera y continuar o informar de un periodo de inactividad. Cuando termines, cierra el socket del cliente aceptado y el socket de escucha del servidor. Este ajuste no controla cuánto tiempo puede permanecer abierta la conexión aceptada ni cuánto pueden esperar las lecturas en ella.

Este ejemplo independiente no tiene ningún cliente que intente conectarse. Muestra cómo vence el plazo de aceptación y comprueba que el socket de escucha sigue abierto después. El código y las etiquetas o salidas visibles del ejemplo se mantienen en inglés.

import java.net.ServerSocket;
import java.net.SocketTimeoutException;

public class AcceptDeadline {
    public static void main(String[] args) throws Exception {
        try (ServerSocket server = new ServerSocket(0)) {
            server.setSoTimeout(100);
            try {
                server.accept();
                System.out.println("Unexpected client connection");
            } catch (SocketTimeoutException timeout) {
                System.out.println("Accept deadline reached");
            }
            System.out.println("Listening socket open: " + !server.isClosed());
        }
    }
}
Accept deadline reached
Listening socket open: true

La ejecución suministrada terminó con código de salida 0 e imprimió las dos líneas que se muestran. Las etiquetas visibles permanecen en inglés porque el bloque no se modifica.

Configurar tiempos de espera para HttpURLConnection

Para una conexión URL abierta como HttpURLConnection, llama a HttpURLConnection.setConnectTimeout para limitar el tiempo de establecimiento de la conexión y a setReadTimeout para limitar la espera de los datos de respuesta. Por ejemplo, después de convertir url.openConnection() a HttpURLConnection, usa connection.setConnectTimeout(8000); y connection.setReadTimeout(10000); antes de conectar o leer. Los valores se expresan en milisegundos; cero significa que no hay límite de tiempo. Lee la respuesta y cierra su flujo con try-with-resources; después, llama a disconnect() cuando la solicitud haya terminado. Captura SocketTimeoutException para gestionar específicamente los tiempos de espera agotados y otras excepciones IOException para tratar errores de red más generales. El nombre exacto de la clase es HttpURLConnection.

Gestionar SocketTimeoutException y verificar la solución

Captura SocketTimeoutException alrededor de la operación que puede agotar el tiempo de espera y registra contexto útil, como el host, el puerto, la operación, el límite configurado y el tiempo transcurrido. No repitas la operación indefinidamente sin informar: aplica una política de reintentos limitada solo cuando sea seguro repetirla y mantén cada intento dentro del plazo total de la solicitud. Cierra los sockets y los flujos en bloques finally o con try-with-resources para que un intento fallido no deje recursos abiertos.

Después de hacer un cambio, verifícalo desde el mismo entorno de ejecución donde ocurrió el fallo. Confirma la dirección y el puerto, comprueba que el servidor esté escuchando y sea accesible, y prueba la operación específica: establecer la conexión, leer o aceptar. Compara el tiempo medido con el límite configurado y revisa los registros del servidor. Si aumentar el tiempo de espera solo retrasa el mismo fallo, restablece un límite adecuado e investiga la ruta, los filtros, el estado del servicio de escucha o la disponibilidad del servicio. Una prueba correcta contra un servidor local no demuestra que se pueda acceder a un extremo remoto ni a la red de producción.