web-dev-qa-db-fra.com

Comment éviter une NoRouteToHostException?

Divulgation: le code sur lequel je travaille est pour les cours universitaires.

Contexte: La tâche que j'essaie de réaliser est de rendre compte de l'effet des différentes techniques de filetage. Pour ce faire, j'ai écrit plusieurs classes qui répondent à une demande d'un client en utilisant Java Sockets. L'idée est d'inonder le serveur de demandes et de rendre compte de la façon dont les différentes stratégies de threading y font face. Chacune le client fera 100 demandes, et à chaque itération nous augmentons le nombre de clients de 50 jusqu'à ce que quelque chose se casse.

Problème: de manière répétée et cohérente, une exception se produit:

 Causée par: Java.net.NoRouteToHostException: impossible d'attribuer l'adresse demandée 
 À Java.net.PlainSocketImpl.socketConnect (méthode native) 
 À Java.net.PlainSocketImpl.doConnect (PlainSocketImpl .Java: 333) 

Cela se produit dans plusieurs scénarios, notamment lorsque le client et le serveur s'exécutent sur localhost. Les connexions peuvent être établies avec succès pendant un certain temps, c'est peu après avoir essayé de connecter 150 clients que l'exception est levée.

Ma première pensée a été que cela pourrait être la limite de Linux sur les descripteurs de fichiers ouverts (1024) mais je ne pense pas. J'ai également vérifié que toutes les connexions entre les sockets sont correctement fermées (c'est-à-dire dans un bloc finally correct).

J'hésite à publier le code car je ne sais pas quelles parties seraient les plus pertinentes et je ne veux pas avoir une énorme liste de code dans la question.

Est-ce que quelqu'un à déjà rencontré cela avant? Comment puis-je éviter l'exception NoRouteToHostException?


EDIT (les autres questions sont en italique)

Quelques bonnes réponses jusqu'à présent qui pointent soit sur la gamme de ports éphémères ou RFC 2780. Les deux suggèrent que j'ai trop de connexions ouvertes. Pour les deux, il semble que le nombre de connexions qui doivent être établies pour atteindre cette limite suggère qu'à un moment donné, je ne ferme pas les connexions.

Après avoir débogué le client et le serveur, il a été observé que les deux frappaient l'appel de méthode myJava-Net-SocketInstance.close(). Cela suggère que les connexions sont fermées (au moins dans le cas non exceptionnel). Est-ce une bonne suggestion?

En outre, y a-t-il une attente au niveau du système d'exploitation requise pour que les ports redeviennent disponibles? Il serait possible d'exécuter le programme une heure distincte pour chaque 50+ clients si cela ne demandait qu'une courte période (ou de façon optimiste, exécuter une commande) avant d'exécuter la prochaine tentative.


EDIT v2.0

Après avoir pris les bonnes réponses fournies, j'ai modifié mon code pour utiliser la méthode setReuseAddress (true) avec chaque connexion Socket établie sur le client. Cela n'a pas eu l'effet souhaité, et je suis toujours limité à 250-300 clients. Une fois le programme terminé, exécutez la commande netstat -a montre qu'il y a beaucoup de connexions de socket dans l'état TIME_WAIT.

Mon hypothèse était que si un socket était dans le TIME-WAIT status, et avait été défini avec le SO-REUSEADDR option, tous les nouveaux sockets tentant d'utiliser ce port pourraient - cependant, je reçois toujours l'exception NoRouteToHostException.

Est-ce correct?Y a-t-il autre chose qui peut être fait pour résoudre ce problème?

59
Grundlefleck

Avez-vous essayé de définir:

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

et/ou

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

Ces paramètres peuvent obliger Linux à réutiliser les sockets TIME_WAIT. Malheureusement, je ne trouve aucune documentation définitive.

52
atomice

Cela peut aider:

La gamme de ports éphémères

Une autre ramification importante de la gamme de ports éphémères est qu'elle limite le nombre maximum de connexions d'une machine à un service spécifique sur une machine distante! Le protocole TCP/IP utilise le 4-Tuple de connexion pour distinguer les connexions, donc si la plage de ports éphémères n'est que de 4000 ports de large, cela signifie qu'il ne peut y avoir que 4000 connexions uniques d'une machine cliente à un service distant à la fois.

Alors peut-être que vous manquez de ports disponibles. Pour obtenir le nombre de ports disponibles, voir

$ cat /proc/sys/net/ipv4/ip_local_port_range 
32768   61000

La sortie provient de mon système Ubuntu, où j'aurais 28232 ports pour les connexions client. Par conséquent, votre test échouera dès que vous aurez 280+ clients.

16
sfussenegger

Impossible d'attribuer l'adresse demandée est la chaîne d'erreur pour l'erreur EADDRNOTAVAIL.

Je soupçonne que vous manquez de ports source. Il y a 16 383 sockets dans la plage dynamique disponibles pour une utilisation comme port source (voir RFC 278 ). 150 clients * 100 connexions = 15 000 ports - vous atteignez donc probablement cette limite.

9
atomice

Si vous manquez de ports source mais ne maintenez pas réellement autant de connexions ouvertes, définissez le SO_REUSEADDR option de socket. Cela vous permettra de réutiliser les ports locaux qui sont toujours dans le TIME_WAIT Etat.

5
David Joyner

Si vous fermez 500 connexions par seconde, vous manquerez de prises. Si vous vous connectez aux mêmes emplacements (serveurs Web) qui utilisent keepalive, vous pouvez implémenter des pools de connexion, afin de ne pas fermer et rouvrir les sockets.

Cela permettra également d'économiser du processeur.

L'utilisation de tcp_tw_recycle et tcp_tw_reuse peut entraîner des paquets provenant de la connexion précédente, c'est pourquoi il y a une attente d'une minute pour que les paquets soient effacés.

1
user190941

Pour tous les autres utilisateurs Java qui tombent sur cette question, je recommanderais d'utiliser le pool de connexions pour que les connexions soient réutilisées correctement.

0
Bryan Larson