Pourquoi InetAddress.isReachable renvoie-t-il false, alors que je peux cingler l'adresse IP?


96
InetAddress byName = InetAddress.getByName("173.39.161.140");
System.out.println(byName);
System.out.println(byName.isReachable(1000));

Pourquoi isReachablerevient-il false? Je peux cingler l'IP.



1
c'est pareil. mais je ne trouve aucun indice pour résoudre le problème, alors je l'ai rappelé ici. Merci pour votre rappel!
jiafu

2
J'essaierais d'augmenter le délai.
jayunit100

c'est une très bonne question, il n'y a pas assez de votes positifs. La seule question similaire que j'ai trouvée a été étiquetée comme une question clojure et la réponse n'a pas été concluante.
jayunit100

3
quelqu'un peut-il me dire ce que fait exactement isReachable ()? il me retourne faux même sur localhost ...
Seth Keno

Réponses:


73

La méthode "isReachable" n'a pas été digne d'être utilisée pour moi dans de nombreux cas. Vous pouvez faire défiler vers le bas pour voir mon alternative pour simplement tester si vous êtes en ligne et capable de résoudre des hôtes externes (ie google.com) ... Ce qui semble généralement fonctionner sur les machines * NIX.

Le problème

Il y a beaucoup de discussions à ce sujet:

Partie 1: Un exemple reproductible du problème

Notez que dans ce cas, il échoue.

       //also, this fails for an invalid address, like "www.sjdosgoogle.com1234sd" 
       InetAddress[] addresses = InetAddress.getAllByName("www.google.com");
      for (InetAddress address : addresses) {
        if (address.isReachable(10000))
        {   
           System.out.println("Connected "+ address);
        }
        else
        {
           System.out.println("Failed "+address);
        }
      }
          //output:*Failed www.google.com/74.125.227.114*

Partie 2: Une solution de contournement hackish

Comme alternative, vous pouvez faire ceci:

// in case of Linux change the 'n' to 'c'
    Process p1 = java.lang.Runtime.getRuntime().exec("ping -n 1 www.google.com");
    int returnVal = p1.waitFor();
    boolean reachable = (returnVal==0);

L'option -c de ping permettra à ping d'essayer simplement d'atteindre le serveur une fois (par opposition au ping infini que nous avons l'habitude d'utiliser sur le terminal).

Cela renverra 0 si l'hôte est accessible . Sinon, vous obtiendrez "2" comme valeur de retour.

Beaucoup plus simple - mais bien sûr, c'est spécifique à la plate-forme. Et il peut y avoir certaines réserves de privilèges à utiliser cette commande - mais je trouve que cela fonctionne sur mes machines.


VEUILLEZ noter que: 1) Cette solution n'est pas une qualité de production. C'est un peu un hack. Si Google est en panne, ou votre Internet est temporairement lent, ou peut-être même si vos privilèges / paramètres système sont amusants, cela pourrait renvoyer de faux négatifs (c'est-à-dire que cela pourrait échouer même si l'adresse d'entrée est accessible). 2) L'échec isReachable est un problème en suspens. Encore une fois - il existe plusieurs ressources en ligne indiquant qu'il n'y a pas de moyen «parfait» de le faire au moment de la rédaction de cet article, en raison de la façon dont la JVM tente d'atteindre les hôtes - je suppose que c'est une tâche intrinsèquement spécifique à la plate-forme qui, bien que simple , n'a pas encore été suffisamment abstraite par la JVM.


@ jayunit100 car aucune des deux approches ne fonctionne, si j'échoue isReachableet que j'utilise le ping, je reçois icmp non autorisé? Savez-vous comment y faire face maintenant?
Yuvi

6
@Yuvi Si vous utilisez Windows, les indicateurs diffèrent. Au lieu de -c, vous voulez -n.
James T Snell

waitFor () prend 10 secondes pour revenir. Existe-t-il un moyen de mentionner le délai d'expiration dans Java 7?
Jaydev

@Jaydev, il y a aussi une option surchargée: public boolean waitFor(long timeout, TimeUnit unit)dans java.lang.Process (@since 1.8)
indivisible

À partir de 2020-01, je peux exécuter l'exemple du problème sans aucun problème, il signale «Connecté» pour une adresse IPv4 et une adresse IPv6. Le bogue est également corrigé dans Java 5.0 b22. Peut isReachable- être est-ce plus fiable de nos jours.
Lii

54

Je suis venu ici pour obtenir une réponse à cette même question, mais je n'étais satisfait d'aucune des réponses car je cherchais une solution indépendante de la plate-forme. Voici le code que j'ai écrit et est indépendant de la plate-forme, mais nécessite des informations sur tout port ouvert sur l'autre machine (que nous avons la plupart du temps).

private static boolean isReachable(String addr, int openPort, int timeOutMillis) {
    // Any Open port on other machine
    // openPort =  22 - ssh, 80 or 443 - webserver, 25 - mailserver etc.
    try {
        try (Socket soc = new Socket()) {
            soc.connect(new InetSocketAddress(addr, openPort), timeOutMillis);
        }
        return true;
    } catch (IOException ex) {
        return false;
    }
}

2
C'est une excellente solution indépendante de la plateforme, merci!
ivandov

1
Je suis content d'avoir fini ici, belle façon de penser Sourabh :)
JustADev

Donc, juste pour être sûr de bien comprendre: tant qu'il n'y a pas d'exception, l'adresse était accessible?
Timmiej93

1
Ceci est identique à ce que fait InetAddress.isReachable()déjà, via le port 7, sauf que ce dernier est plus intelligent sur ce que les différents possibles IOExceptionssignifient en termes d'accessibilité.
Marquis of Lorne

1
Oui @EJP Je pense que ce serait génial s'il InetAddress.isReachable()était surchargé d'inclure l'argument port, dans la bibliothèque standard. Je me demande pourquoi il n'a pas été inclus?
Sourabh Bhat

7

Si vous souhaitez uniquement vérifier s'il est connecté à Internet, utilisez cette méthode, il renvoie true si Internet est connecté, il est préférable si vous utilisez l'adresse du site que vous essayez de vous connecter via le programme.

     public static boolean isInternetReachable()
    {
        try {
            //make a URL to a known source
            URL url = new URL("http://www.google.com");

            //open a connection to that source
            HttpURLConnection urlConnect = (HttpURLConnection)url.openConnection();

            //trying to retrieve data from the source. If there
            //is no connection, this line will fail
            Object objData = urlConnect.getContent();

        } catch (Exception e) {              
            e.printStackTrace();
            return false;
        }

        return true;
    }

2
Inutile si le serveur n'a pas de serveur Web, ce n'est pas du tout une condition spécifiée dans la question.
Fran Marzoa

3

Il suffit de le mentionner explicitement puisque les autres réponses ne le font pas. La partie ping de isReachable () nécessite un accès root sous Unix. Et comme indiqué par bestsss dans 4779367 :

Et si vous demandez pourquoi le ping de bash ne le fait pas, il en a également besoin. Faites cela ls -l / bin / ping.

Étant donné que l'utilisation de root n'était pas une option dans mon cas, la solution était d'autoriser l'accès au port 7 du pare-feu au serveur spécifique qui m'intéressait.


3

Je ne sais pas dans quel état était la question lorsque la question initiale a été posée en 2012.

Dans l'état actuel des choses, le ping sera exécuté en tant que root. Grâce à l'autorisation de l'exécutable ping, vous verrez l'indicateur + s et le processus appartenant à root, ce qui signifie qu'il fonctionnera en tant que root. lancez ls -liat sur l'emplacement du ping et vous devriez le voir.

Ainsi, si vous exécutez InetAddress.getByName ("www.google.com"). IsReacheable (5000) en tant que root, il doit renvoyer true.

vous avez besoin des autorisations appropriées pour le socket brut, qui est utilisé par ICMP (le protocole utilisé par ping)

InetAddress.getByName est aussi fiable que ping, mais vous avez besoin des autorisations appropriées sur le processus pour qu'il fonctionne correctement.


0

Je suggérerais que le SEUL moyen fiable de tester une connexion Internet est de se connecter ET de télécharger un fichier, OU d'analyser la sortie d'un appel ping du système d'exploitation via exec (). Vous ne pouvez pas compter sur le code de sortie pour le ping et isReachable () est de la merde.

Vous ne pouvez pas vous fier à un code de sortie ping car il renvoie 0 si la commande ping s'exécute correctement. Malheureusement, le ping s'exécute correctement s'il ne peut pas atteindre l'hôte cible mais obtient un «hôte de destination inaccessible» de votre routeur ADSL domestique. C'est une sorte de réponse qui est traitée comme un succès, donc le code de sortie = 0. Il faut ajouter cependant que c'est sur un système Windows. Non vérifié * nixes.


0
 private boolean isReachable(int nping, int wping, String ipping) throws Exception {

    int nReceived = 0;
    int nLost = 0;

    Runtime runtime = Runtime.getRuntime();
    Process process = runtime.exec("ping -n " + nping + " -w " + wping + " " + ipping);
    Scanner scanner = new Scanner(process.getInputStream());
    process.waitFor();
    ArrayList<String> strings = new ArrayList<>();
    String data = "";
    //
    while (scanner.hasNextLine()) {
        String string = scanner.nextLine();
        data = data + string + "\n";
        strings.add(string);
    }

    if (data.contains("IP address must be specified.")
            || (data.contains("Ping request could not find host " + ipping + ".")
            || data.contains("Please check the name and try again."))) {
        throw new Exception(data);
    } else if (nping > strings.size()) {
        throw new Exception(data);
    }

    int index = 2;

    for (int i = index; i < nping + index; i++) {
        String string = strings.get(i);
        if (string.contains("Destination host unreachable.")) {
            nLost++;
        } else if (string.contains("Request timed out.")) {
            nLost++;
        } else if (string.contains("bytes") && string.contains("time") && string.contains("TTL")) {
            nReceived++;
        } else {
        }
    }

    return nReceived > 0;
}

nping est le nombre d'essais de ping sur ip (paquets), si vous avez un réseau ou des systèmes occupés, choisissez des nombres plus élevés.
wping est le temps d'attente pour pong d'ip, vous pouvez le définir à 2000 ms
pour utiliser cette méthode, vous pouvez écrire ceci:

isReachable(5, 2000, "192.168.7.93");

injection de commande potentielle, assurez-vous de valider l'entrée de chaîne avant d'exécuter ceci
espion

il y a aussi le message d'erreur "échec général". Cela ressemble également à des fenêtres spécifiques. Je suis presque sûr que ces messages d'erreur sont localisés dans différentes langues, donc pas super portables.
espion le

ceci est pour les systèmes Windows.
Armin Mokri

yup je l'ai déjà vu sur windows
espion

0

Ou en utilisant cette façon:

public static boolean exists(final String host)
{
   try
   {
      InetAddress.getByName(host);
      return true;
   }
   catch (final UnknownHostException exception)
   {
      exception.printStackTrace();
      // Handler
   }
   return false;
}

0

InetAddress.isReachable est flappy, et renvoie parfois inaccessible pour les adresses auxquelles nous pouvons envoyer un ping.

J'ai essayé ce qui suit:

ping -c 1 <fqdn>et vérifiez le exit status.

Fonctionne pour tous les cas que j'avais essayés où InetAddress.isReachablene fonctionne pas.


-1

Étant donné que vous pouvez exécuter une requête ping sur l'ordinateur, votre processus Java doit s'exécuter avec des privilèges suffisants pour effectuer la vérification. Probablement en raison de l'utilisation de ports dans la plage inférieure. Si vous exécutez votre programme java avec sudo / superuser, je parie que cela fonctionne.


Vous n'avez pas besoin de privilège pour vous connecter aux ports dans la plage basse.
Marquis of Lorne
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.