Por que InetAddress.isReachable retorna falso, quando posso executar ping no endereço IP?

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

Por que isReachableretorna false? Posso fazer ping no IP.

Jiafu
fonte
Possível duplicata: stackoverflow.com/questions/4779367/…
assylias
1
é semelhante. mas não consigo encontrar nenhuma pista para resolver o problema. Então, aumentei novamente aqui. Obrigado pelo seu lembrete!
jiafu,
2
Eu tentaria aumentar o tempo limite.
jayunit100
esta é uma pergunta muito boa, não há votos positivos suficientes. A única pergunta semelhante que encontrei foi marcada como uma pergunta clojure e a resposta foi inconclusiva.
jayunit100
3
alguém pode me dizer o que exatamente isReachable () faz? retorna falso mesmo no localhost ...
Seth Keno

Respostas:

73

O método "isReachable" não vale a pena usar para mim em muitos casos. Você pode rolar até a parte inferior para ver minha alternativa para simplesmente testar se você está online e é capaz de resolver hosts externos (por exemplo, google.com) ... O que geralmente parece funcionar em máquinas * NIX.

O problema

Fala-se muito sobre isso:

Parte 1: um exemplo reproduzível do problema

Observe que, neste caso, ele falha.

       //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: Uma Solução Alternativa Hackish

Como alternativa, você pode fazer o seguinte:

// 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);

A opção -c de ping permitirá que o ping simplesmente tente alcançar o servidor uma vez (ao contrário do ping infinito que estamos acostumados a usar no terminal).

Isso retornará 0 se o host estiver acessível . Caso contrário, você obterá "2" como valor de retorno.

Muito mais simples - mas é claro que é específico da plataforma. E pode haver certas advertências de privilégio para usar esse comando - mas acho que funciona em minhas máquinas.


POR FAVOR, observe que: 1) Esta solução não é qualidade de produção. É um pouco hack. Se o Google estiver fora do ar, ou sua internet estiver temporariamente lenta, ou talvez até mesmo se houver alguma graça em seus privilégios / configurações do sistema, ele pode retornar falsos negativos (ou seja, pode falhar mesmo que o endereço de entrada seja acessível). 2) A falha isReachable é um problema pendente. Novamente - existem vários recursos online indicando que não existe uma maneira "perfeita" de fazer isso no momento da redação deste artigo, devido à maneira como a JVM tenta alcançar os hosts - acho que é uma tarefa intrinsecamente específica da plataforma que, embora simples , ainda não foi suficientemente abstraído pela JVM.

Jayunit100
fonte
@ jayunit100 para nenhuma das abordagens está funcionando, isReachableestou obtendo falha e usando ping estou recebendo icmp não permitido? Você sabe como lidar com isso agora?
Yuvi
6
@Yuvi Se você estiver usando o Windows, os sinalizadores são diferentes. Em vez de -c, você deseja -n.
James T Snell
waitFor () leva 10 segundos para retornar. Existe uma maneira de mencionar o tempo limite no Java 7?
Jaydev de
@Jaydev, também há uma opção sobrecarregada: public boolean waitFor(long timeout, TimeUnit unit)em java.lang.Process (@since 1.8)
indivisível de
A partir de 2020-01 posso executar o exemplo do problema sem problemas, ele informa "Conectado" para um endereço IPv4 e um endereço IPv6. O bug também foi corrigido no Java 5.0 b22. Talvez isReachableseja mais confiável hoje em dia.
Lii
54

Vim aqui para obter uma resposta para esta mesma pergunta, mas não fiquei satisfeito com nenhuma das respostas porque estava à procura de uma solução independente de plataforma. Aqui está o código que escrevi e é independente de plataforma, mas requer informações sobre qualquer porta aberta na outra máquina (que temos na maioria das vezes).

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;
    }
}
Sourabh Bhat
fonte
2
Esta é uma ótima solução independente de plataforma, obrigado!
ivandov
1
Estou feliz por ter acabado aqui, boa maneira de pensar Sourabh :)
JustADev
Então, só para ter certeza de que entendi perfeitamente: Contanto que não haja exceção, o endereço estava acessível?
Timmiej93
1
Isso é idêntico ao que InetAddress.isReachable()já faz, via porta 7, exceto que esta última é mais inteligente sobre o que as várias possíveis IOExceptionssignificam em termos de acessibilidade.
Marquês de Lorne
1
Sim @EJP acho que seria ótimo se InetAddress.isReachable()fosse sobrecarregado para incluir o argumento da porta, na biblioteca padrão. Eu me pergunto por que não foi incluído?
Sourabh Bhat
7

Se você deseja apenas verificar se ele está conectado à internet use este método, retorna verdadeiro se a internet estiver conectada, é preferível se você usar o endereço do site que está tentando conectar através do programa.

     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;
    }
CleanX
fonte
2
Inútil se o servidor não tiver um servidor web, essa não é uma condição especificada na pergunta.
Fran Marzoa
3

Apenas mencionando explicitamente, já que as outras respostas não o fazem. A parte ping de isReachable () requer acesso root no Unix. E como apontado por bestsss em 4779367 :

E se você perguntar por que o ping do bash não funciona, na verdade ele também precisa. Faça isso ls -l / bin / ping.

Como usar root não era uma opção no meu caso, a solução foi permitir o acesso à porta 7 do firewall para o servidor específico no qual eu estava interessado.

Zitrax
fonte
3

Não tenho certeza de qual era o estado quando a pergunta original foi feita em 2012.

Como está agora, o ping será executado como root. Através da autorização do executável ping você verá o sinalizador + s, e o processo pertencente ao root, o que significa que será executado como root. execute ls -liat onde o ping está localizado e você deverá vê-lo.

Portanto, se você executar InetAddress.getByName ("www.google.com"). IsReacheable (5000) como root, ele deve retornar verdadeiro.

você precisa de autorizações adequadas para o soquete bruto, que é usado pelo ICMP (o protocolo usado pelo ping)

InetAddress.getByName é tão confiável quanto o ping, mas você precisa de permissões adequadas no processo para que ele seja executado corretamente.

Clebert Suconic
fonte
0

Eu sugeriria que a ÚNICA maneira confiável de testar uma conexão com a Internet é realmente conectar E baixar um arquivo OU analisar a saída de uma chamada de ping do sistema operacional via exec (). Você não pode confiar no código de saída para ping e isReachable () é uma porcaria.

Você não pode confiar em um código de saída de ping, pois ele retorna 0 se o comando ping for executado corretamente. Infelizmente, o ping é executado corretamente se não puder alcançar o host de destino, mas obtém um "Host de destino inalcançável" de seu roteador ADSL doméstico. Este é o tipo de resposta que é tratada como uma resposta bem-sucedida, portanto, código de saída = 0. Porém, é preciso acrescentar que se trata de um sistema Windows. Não verificado * nixes.

cossoft
fonte
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 é o número de tentativas de ping de ip (pacotes), se você tiver uma rede ou sistemas ocupados, escolha números nping maiores.
wping é o tempo de espera pelo pong do ip, você pode configurá-lo
para 2.000 ms para usar este método. Você pode escrever isto:

isReachable(5, 2000, "192.168.7.93");
Armin Mokri
fonte
injeção de comando potencial, certifique-se de validar a entrada de string antes de executar isso
spy
há também a mensagem de erro "falha geral". Isso também parece ser específico do Windows. Tenho certeza de que essas mensagens de erro estão localizadas em diferentes idiomas, portanto, não são superportáveis.
espião
isso é para sistemas Windows.
Armin Mokri
Sim, eu já vi isso antes no Windows
espião
0

Ou usando desta forma:

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

InetAddress.isReachable é flappy, e às vezes retorna inacessível para endereços que podemos pingar.

Tentei o seguinte:

ping -c 1 <fqdn>e verifique o exit status.

Funciona para todos os casos que tentei InetAddress.isReachablee não funciona.

Sandeep Sarkar
fonte
-1

Como você pode executar o ping no computador, seu processo Java deve ser executado com privilégios suficientes para realizar a verificação. Provavelmente devido ao uso de portas na faixa inferior. Se você executar seu programa java com sudo / superuser, aposto que funciona.

col
fonte
Você não precisa de privilégios para se conectar a portas na faixa baixa.
Marquês de Lorne