java - unable - ¿Cómo evitar una NoRouteToHostException?
http transport error java net noroutetohostexception no route to host (6)
Esto puede ayudar:
Otra ramificación importante de la gama de puertos efímeros es que limita la cantidad máxima de conexiones de una máquina a un servicio específico en una máquina remota. El protocolo TCP / IP usa la conexión de 4 tuplas para distinguir entre conexiones, por lo que si el rango del puerto efímero es de solo 4000 puertos, significa que solo puede haber 4000 conexiones únicas desde una máquina cliente a un servicio remoto a la vez.
Entonces quizás te quedes sin puertos disponibles. Para obtener la cantidad de puertos disponibles, consulte
$ cat /proc/sys/net/ipv4/ip_local_port_range
32768 61000
La salida es de mi sistema Ubuntu, donde tendría 28,232 puertos para conexiones de clientes. Por lo tanto, su prueba fallará tan pronto como tenga más de 280 clientes.
Divulgación: el código en el que estoy trabajando es para cursos universitarios.
Antecedentes: la tarea que intento completar es informar sobre el efecto de las diferentes técnicas de enhebrado. Para hacer esto, he escrito varias clases que responden a una solicitud de un cliente que usa Java Sockets. La idea es inundar el servidor con solicitudes e informar sobre cómo las diferentes estrategias de enhebrado lidian con esto. Cada cliente hará 100 solicitudes, y en cada iteración aumentaremos la cantidad de clientes en 50 hasta que se rompa algo.
Problema: repetible y consistentemente ocurre una excepción:
Caused by: java.net.NoRouteToHostException: Cannot assign requested address at java.net.PlainSocketImpl.socketConnect(Native Method) at java.net.PlainSocketImpl.doConnect(PlainSocketImpl.java:333)
Esto ocurre en varios escenarios, incluso cuando tanto el cliente como el servidor se ejecutan en localhost. Las conexiones se pueden realizar con éxito durante un tiempo; poco después de tratar de conectar 150 clientes, se produce la excepción.
Lo primero que pensé fue que podría ser el límite de Linux para los descriptores de archivos abiertos (1024), pero no lo creo. También comprobé que todas y cada una de las conexiones entre los sockets estén cerradas correctamente (es decir, dentro de un bloque finally
correcto).
Tengo dudas en publicar el código porque no estoy seguro de qué partes serían las más relevantes, y no quiero tener una gran lista de códigos en la pregunta.
¿Alguien ha encontrado esto antes? ¿Cómo puedo evitar la NoRouteToHostException?
EDITAR (las preguntas adicionales están en cursiva)
Algunas buenas respuestas hasta ahora que apuntan a The Ephemeral Port Range o RFC 2780. Ambos sugieren que tengo demasiadas conexiones abiertas. Para ambos, parece que el número de conexiones que deben realizarse para alcanzar este límite sugiere que en algún momento no cierro las conexiones.
Después de depurar tanto el cliente como el servidor, se ha observado que ambos tocan el método llamado myJava-Net-SocketInstance.close()
. Esto sugeriría que las conexiones se están cerrando (al menos en el caso no excepcional). ¿Es esta una sugerencia correcta?
Además, ¿hay una espera de nivel del sistema operativo necesaria para que los puertos estén disponibles nuevamente? Sería una posibilidad de ejecutar el programa un tiempo separado para cada 50+ clientes si solo requiriera un período corto (u optimista, ejecutando un comando) antes de ejecutar el siguiente intento.
EDITAR v2.0
Habiendo tomado las buenas respuestas provistas, modifiqué mi código para usar el método setReuseAddress (verdadero) con cada conexión de Socket hecha en el cliente. Esto no tuvo el efecto deseado, y todavía estoy limitado a 250-300 clientes. Después de que el programa finaliza, ejecutar el comando netstat -a
muestra que hay muchas conexiones de socket en el estado TIME_WAIT.
Mi suposición era que si un socket estaba en el estado TIME-WAIT
y se había configurado con la opción SO-REUSEADDR
, cualquier nuevo socket que intente usar ese puerto podría, sin embargo, todavía estoy recibiendo NoRouteToHostException.
¿Es esto correcto? ¿Hay algo más que pueda hacerse para resolver este problema?
Has intentado configurar:
echo "1" >/proc/sys/net/ipv4/tcp_tw_reuse
y / o
echo "1" >/proc/sys/net/ipv4/tcp_tw_recycle
Estas configuraciones pueden hacer que Linux reutilice los zócalos TIME_WAIT. Desafortunadamente no puedo encontrar ninguna documentación definitiva.
No se puede asignar la dirección solicitada, es la cadena de error para el error EADDRNOTAVAIL.
Sospecho que te estás quedando sin puertos de origen. Hay 16,383 zócalos en el rango dinámico disponibles para usar como puerto de origen (ver RFC 2780 ). 150 clientes * 100 conexiones = 15,000 puertos, por lo que probablemente esté llegando a este límite.
Para cualquier otro usuario de Java que tropiece con esta pregunta, le recomendaría utilizar la agrupación de conexiones para que las conexiones se reutilicen correctamente.
Si está cerrando 500 conexiones por segundo, se le acabarán los enchufes. Si se está conectando a las mismas ubicaciones (servidores web) que usan keepalive, puede implementar pools de conexión, para que no cierre y vuelva a abrir sockets.
Esto también guardará la CPU.
El uso de tcp_tw_recycle y tcp_tw_reuse puede dar como resultado que los paquetes entren desde la conexión anterior, es por eso que hay una espera de 1 minuto para que los paquetes se borren.
Si se está quedando sin puertos de origen pero en realidad no mantiene tantas conexiones abiertas, configure la SO_REUSEADDR
socket SO_REUSEADDR
. Esto le permitirá reutilizar los puertos locales que todavía están en el estado TIME_WAIT
.