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.



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 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;
    }
}

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;
    }

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.


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.


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.


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

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;
}

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.


-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.


Você não precisa de privilégios para se conectar a portas na faixa baixa.
— Marquês de Lorne
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.