2009-10-15 10 views
52

Divulgación: el código en el que estoy trabajando es para cursos universitarios.Cómo evitar una excepción NoRouteToHostException?

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 consistente, se produce 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, incluyendo cuando tanto el cliente y el servidor se están ejecutando en el servidor local. Las conexiones se pueden realizar con éxito durante un tiempo; poco después de tratar de conectar 150 clientes, se produce la excepción.

Mi primer pensamiento fue que podría ser el límite de Linux para los descriptores de archivos abiertos (1024), pero no lo creo. También verifiqué que todas las conexiones entre los enchufes estuvieran cerradas correctamente (es decir, dentro de un bloque correcto finally).

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 (más preguntas están en cursiva)

Algunas buenas respuestas hasta el momento ese momento ya sea al El efímero de intervalos de puertos o RFC 2780. Ambos de los cuales podría sugerir 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.

Al depurar tanto el cliente como el servidor, se ha observado que ambos métodos presionan la llamada al método 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 un nivel de espera de sistema operativo necesario para que los puertos vuelvan a estar disponibles? 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 proporcionadas, he modificado mi código para utilizar el método de setReuseAddress (verdadero) con cada conexión de socket hecho en el cliente. Esto no tuvo el efecto deseado, y todavía estoy limitado a 250-300 clientes. Una vez que finaliza el programa, ejecutar el comando netstat -a muestra que hay muchas conexiones de socket en el estado TIME_WAIT.

Mi hipótesis era que si un enchufe estaba en el estado TIME-WAIT, y se había establecido con la opción SO-REUSEADDR, los nuevos sockets que intentan utilizar ese puerto serían capaces de - sin embargo, todavía estoy recibiendo el NoRouteToHostException.

¿Es esto correcto? ¿Hay algo más que se pueda hacer para resolver este problema?

+0

@Grundlefleck Si todavía no lo hace, intente llamar al adaptador predeterminado Socket() para recuperar un socket desconectado. Luego setReuseAddress (verdadero). Luego conecta(). Desea decirle a la pila que reutilice la dirección antes de intentar enlazarla. – Duck

+0

Anteriormente estaba llamando al constructor que conecta (Socket (host, port)) y cuando vi tu comentario, se encendió una bombilla, y me di una bofetada por ser un idiota ... pero no funcionó, y el problema sigue siendo :-( – Grundlefleck

Respuesta

9

No se puede asignar la dirección solicitada, es la cadena de error para el error EADDRNOTAVAIL.

Sospecho que se está quedando sin puertos de origen. Hay 16,383 zócalos en el rango dinámico disponibles para usar como puerto de origen (consulte RFC 2780). 150 clientes * 100 conexiones = 15,000 puertos, por lo que probablemente esté llegando a este límite.

16

Esto puede ayudar:

The Ephemeral Port Range

Otra ramificación importante del intervalo de puertos efímeros es que limita el número máximo de conexiones de una máquina a un servicio específico en un remoto ¡máquina! El protocolo TCP/IP utiliza 4-tupla de la conexión a distinguir entre las conexiones, por lo que si el intervalo de puertos efímeros es sólo 4000 puertos de ancho, lo que significa que no puede sólo será 4000 conexiones únicas de una máquina cliente a una servicio remoto al una vez.

Así que tal vez se quede sin puertos disponibles. Para obtener el número 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 cliente. Por lo tanto, su prueba fallará tan pronto como tenga más de 280 clientes.

+0

O puede haber alcanzado el límite máximo en los descriptores de archivos. Consulte ulimit en Linux o launchtl en MacOS (http://docs.basho.com/riak/latest/ops/tuning/open-files-limit/# Mac-OS-X) –

5

Si se está quedando sin puertos de origen pero en realidad no mantiene tantas conexiones abiertas, configure la opción de socket SO_REUSEADDR. Esto le permitirá reutilizar los puertos locales que todavía están en el estado TIME_WAIT.

+0

Disculpas por mantenerlo en un nivel Java. Cuando instancia el socket, también llamo a mySocket.setReuseAddress (true); , que no resuelve el problema. para usar esto? – Grundlefleck

+1

No. Si no lo haces explicity bind() la pila lo hará por ti durante connect(). –

45

Ha intentado ajuste:

echo "1" >/proc/sys/net/ipv4/tcp_tw_reuse 

y/o

echo "1" >/proc/sys/net/ipv4/tcp_tw_recycle 

Estos ajustes pueden hacer que Linux re-utilizar las tomas TIME_WAIT. Desafortunadamente no puedo encontrar ninguna documentación definitiva.

+5

Lo siento, tengo que decirlo: ¡TE AMO! – tsykora

+0

@atomice Amigo, muchas gracias. He estado enfrentando este problema desde hace una semana. finalmente me salvó. –

+0

Esto resolvió un desagradable problema que nos estaba molestando durante meses. –

0

Para cualquier otro usuario de Java que tropiece con esta pregunta, recomendaría usar la agrupación de conexiones para que las conexiones se reutilicen correctamente.

+2

La agrupación de conexiones c3p0 es para la agrupación de conexiones JDBC, que no es el tipo de conexiones de las que se trata la pregunta. – Bernie

+0

referencia c3p0 eliminada, y la agrupación de conexiones es una opción válida para mitigar este problema –

1

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 ahorrará también 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.

Cuestiones relacionadas