Perché InetAddress.isReachable restituisce false, quando posso eseguire il ping dell'indirizzo IP?


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

Perché isReachableritorna false? Posso eseguire il ping dell'IP.



1
è simile. ma non riesco a trovare alcun indizio per risolvere il problema, quindi ho rilanciato qui. Grazie per il tuo promemoria!
jiafu

2
Proverei ad aumentare il timeout.
jayunit100

questa è un'ottima domanda, non ci sono abbastanza voti positivi. L'unica domanda simile che ho trovato è stata contrassegnata come domanda di chiusura e la risposta è stata inconcludente.
jayunit100

3
qualcuno può dirmi cosa fa esattamente isReachable ()? mi restituisce falso anche su localhost ...
Seth Keno

Risposte:


73

Il metodo "isReachable" non è stato degno di essere utilizzato in molti casi. Puoi scorrere fino in fondo per vedere la mia alternativa per testare semplicemente se sei online e in grado di risolvere host esterni (ad esempio google.com) ... Che generalmente sembra funzionare su macchine * NIX.

Il problema

Ci sono molte chiacchiere su questo:

Parte 1: un esempio riproducibile del problema

Nota che in questo caso fallisce.

       //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*

Parte 2: una soluzione alternativa alla pirateria informatica

In alternativa, puoi farlo:

// 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'opzione -c di ping consentirà a ping di tentare semplicemente di raggiungere il server una volta (al contrario del ping infinito che siamo abituati a usare al terminale).

Questo restituirà 0 se l'host è raggiungibile . Altrimenti, otterrai "2" come valore di ritorno.

Molto più semplice, ma ovviamente è specifico della piattaforma. E potrebbero esserci alcuni avvertimenti sui privilegi nell'utilizzo di questo comando, ma trovo che funzioni sulle mie macchine.


SI PREGA di notare che: 1) Questa soluzione non è la qualità della produzione. È un po 'un trucco. Se Google è inattivo, o la tua connessione Internet è temporaneamente lenta, o forse anche se c'è qualche stranezza nei tuoi privilegi / impostazioni di sistema, potrebbe restituire falsi negativi (cioè potrebbe non riuscire anche se l'indirizzo di input è raggiungibile). 2) L'errore isReachable è un problema in sospeso. Ancora una volta - ci sono diverse risorse online che indicano che non esiste un modo "perfetto" per farlo al momento della stesura di questo articolo, a causa del modo in cui la JVM cerca di raggiungere gli host - immagino che sia un'attività intrinsecamente specifica della piattaforma che, sebbene semplice , non è stato ancora sufficientemente astratto dalla JVM.


@ jayunit100 per nessuno degli approcci sta funzionando, isReachablese sto fallendo e usando ping ottengo icmp non consentito? Sai come affrontarlo adesso?
Yuvi

6
@Yuvi Se stai usando Windows, i flag sono diversi. Invece di -c vuoi -n.
James T Snell

waitFor () impiega 10 secondi per tornare. C'è un modo per menzionare il timeout in Java 7?
Jaydev

@Jaydev, c'è anche un'opzione di sovraccarico: public boolean waitFor(long timeout, TimeUnit unit)in java.lang.Process (@since 1.8)
indivisibile

A partire dal 2020-01 posso eseguire l'esempio del problema senza problemi, segnala "Connesso" per un indirizzo IPv4 e un indirizzo IPv6. Il bug è stato corretto anche in Java 5.0 b22. Forse isReachableè più affidabile oggigiorno.
Lii

54

Sono venuto qui per ottenere una risposta alla stessa domanda, ma nessuna delle risposte mi ha soddisfatto perché stavo cercando una soluzione indipendente dalla piattaforma. Ecco il codice che ho scritto ed è indipendente dalla piattaforma, ma richiede informazioni su qualsiasi porta aperta sull'altra macchina (che abbiamo la maggior parte del tempo).

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
Questa è un'ottima soluzione indipendente dalla piattaforma, grazie!
ivandov

1
Sono felice di essere finito qui, bel modo di pensare Sourabh :)
JustADev

Quindi, solo per assicurarmi di aver capito completamente: finché non ci sono eccezioni, l'indirizzo era raggiungibile?
Timmiej93

1
Questo è identico a quanto InetAddress.isReachable()già fa, tramite la porta 7, tranne che quest'ultima è più intelligente su cosa IOExceptionssignificano i vari possibili in termini di raggiungibilità.
Marchese di Lorne

1
Sì @EJP Penso che sarebbe fantastico se InetAddress.isReachable()fosse sovraccaricato per includere l'argomento port, nella libreria standard. Mi chiedo perché non fosse incluso?
Sourabh Bhat

7

Se vuoi solo controllare se è connesso ad internet usa questo metodo, restituisce vero se internet è connesso, è preferibile se usi l'indirizzo del sito che stai cercando di connetterti tramite il programma.

     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 se il server non ha un server web, non è affatto una condizione specificata nella domanda.
Fran Marzoa

3

Menzionandolo esplicitamente poiché le altre risposte no. La parte ping di isReachable () richiede l'accesso come root su Unix. E come sottolineato da bestsss in 4779367 :

E se chiedi perché ping da bash non lo fa, in realtà ne ha bisogno. Fallo ls -l / bin / ping.

Poiché l'utilizzo di root non era un'opzione nel mio caso, la soluzione era consentire l'accesso alla porta 7 nel firewall al server specifico a cui ero interessato.


3

Non sono sicuro di quale fosse lo stato quando è stata posta la domanda originale nel 2012.

Allo stato attuale, il ping verrà eseguito come root. Tramite l'autorizzazione dell'eseguibile ping vedrai il flag + se il processo che appartiene a root, il che significa che verrà eseguito come root. esegui ls -liat su dove si trova il ping e dovresti vederlo.

Quindi, se esegui InetAddress.getByName ("www.google.com"). IsReacheable (5000) come root, dovrebbe restituire true.

sono necessarie le autorizzazioni appropriate per il raw socket, che viene utilizzato da ICMP (il protocollo utilizzato da ping)

InetAddress.getByName è affidabile come il ping, ma sono necessarie le autorizzazioni appropriate per il processo per farlo funzionare correttamente.


0

Suggerirei che l'UNICO modo affidabile per testare una connessione Internet è di connettersi e scaricare effettivamente un file, OPPURE analizzare l'output di una chiamata ping del sistema operativo tramite exec (). Non puoi fare affidamento sul codice di uscita per il ping e isReachable () è una schifezza.

Non puoi fare affidamento su un codice di uscita ping poiché restituisce 0 se il comando ping viene eseguito correttamente. Sfortunatamente, il ping viene eseguito correttamente se non riesce a raggiungere l'host di destinazione ma ottiene un "Host di destinazione irraggiungibile" dal router ADSL di casa. Questa è una specie di risposta che viene trattata come un successo di successo, quindi il codice di uscita = 0. Devo aggiungere però che questo è su un sistema Windows. Non verificato * 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 è il numero di tentativi di ping ip (pacchetti), se hai una rete occupata o sistemi scegli numeri di nping più grandi.
wping è tempo di attesa di pong da ip, puoi impostarlo a 2000 ms
per usare questo metodo puoi scrivere questo:

isReachable(5, 2000, "192.168.7.93");

potenziale iniezione di comando, assicurarsi di convalidare la stringa di input prima di eseguire questo
spia il

c'è anche il messaggio di errore "errore generale". Anche questo sembra specifico per Windows. Sono abbastanza sicuro che questi messaggi di errore siano localizzati in lingue diverse, quindi non super portabili.
spia il

questo è per i sistemi Windows.
Armin Mokri

sì, l'ho già visto su Windows
spia il

0

O usando in questo modo:

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 è instabile e talvolta restituisce irraggiungibile per gli indirizzi che possiamo eseguire il ping.

Ho provato quanto segue:

ping -c 1 <fqdn>e controlla il file exit status.

Funziona per tutti i casi che ho provato dove InetAddress.isReachablenon funziona.


-1

Poiché è possibile eseguire il ping del computer, il processo Java dovrebbe essere eseguito con privilegi sufficienti per eseguire il controllo. Probabilmente a causa dell'utilizzo di porte nella fascia inferiore. Se esegui il tuo programma java con sudo / superuser, scommetto che funziona.


Non è necessario il privilegio per connettersi alle porte nell'intervallo basso.
Marchese di Lorne
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.