Почему InetAddress.isReachable возвращает false, если я могу пропинговать IP-адрес?

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

Почему isReachableвозвращается false? Я могу пропинговать IP.

цзяфу
источник
Возможный дубликат: stackoverflow.com/questions/4779367/…
assylias
1
похоже. но я не могу найти ключ к решению проблемы, поэтому я повторно поднял его здесь. Спасибо за напоминание!
jiafu
2
Я бы попробовал увеличить таймаут.
jayunit100
это очень хороший вопрос, недостаточно голосов. Единственный подобный вопрос, который я нашел, был помечен как закрывающий, и ответ был неубедительным.
jayunit100
3
может кто-нибудь сказать мне, что именно делает isReachable ()? он возвращает мне ложь даже на локальном хосте ...
Сет Кено

Ответы:

73

Метод isReachable во многих случаях мне не стоил использовать. Вы можете прокрутить вниз, чтобы увидеть мою альтернативу для простого тестирования, если вы в сети и способны ли разрешать внешние хосты (например, google.com) ... Что обычно работает на машинах * NIX.

Проблема

Об этом много говорят:

Часть 1: воспроизводимый пример проблемы

Обратите внимание, что в этом случае это не удается.

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

Часть 2: Хакерский обходной путь

В качестве альтернативы вы можете сделать это:

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

Параметр -c команды ping позволит ping просто попытаться связаться с сервером один раз (в отличие от бесконечного ping, который мы привыкли использовать на терминале).

Это вернет 0, если хост доступен . В противном случае вы получите «2» в качестве возвращаемого значения.

Гораздо проще, но, конечно, зависит от платформы. И при использовании этой команды могут быть определенные привилегии, но я считаю, что она работает на моих машинах.


ПОЖАЛУЙСТА, обратите внимание, что: 1) Это решение не является производственным. Это что-то вроде взлома. Если Google не работает, или ваш интернет временно замедлен, или, может быть, даже если в ваших привилегиях / системных настройках есть некоторая забавность, if может вернуть ложноотрицательные результаты (т.е. он может выйти из строя, даже если входной адрес доступен). 2) Ошибка isReachable - нерешенная проблема. Опять же - есть несколько онлайн-ресурсов, указывающих на то, что на момент написания этой статьи не было «идеального» способа сделать это из-за того, как JVM пытается достичь хостов - я предполагаю, что это внутренне зависящая от платформы задача, которая, хотя и проста , JVM еще недостаточно абстрагировала.

jayunit100
источник
@ jayunit100, поскольку ни один из подходов не работает, или isReachableя получаю ошибку, и с помощью ping я получаю icmp не разрешено? Вы знаете, как теперь с этим бороться?
Yuvi 07
6
@Yuvi Если вы используете Windows, флаги различаются. Вместо -c вы хотите -n.
Джеймс Т. Снелл,
Для возврата waitFor () требуется 10 секунд. Есть ли способ упомянуть тайм-аут в Java 7?
Jaydev
@Jaydev, есть и перегруженный вариант: public boolean waitFor(long timeout, TimeUnit unit)в java.lang.Process (@since 1.8)
неделимый
По состоянию на 2020-01 год я могу запустить пример проблемы без каких-либо проблем, он сообщает «Подключено» для одного адреса IPv4 и одного адреса IPv6. Ошибка также исправлена в Java 5.0 B22. Возможно, сейчас isReachableэто более надежно.
Lii
54

Я пришел сюда, чтобы получить ответ на тот же вопрос, но меня не удовлетворил ни один из ответов, потому что я искал независимое от платформы решение. Вот код, который я написал и не зависит от платформы, но требует информации о любом открытом порте на другом компьютере (который у нас есть большую часть времени).

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
Это отличное платформо-независимое решение, спасибо!
ivandov
1
Я счастлив, что оказался здесь, хороший образ мыслей, Сураб :)
JustADev
Итак, просто чтобы убедиться, что я полностью понимаю: если нет исключения, адрес был доступен?
Timmiej93
1
Это идентично тому, что InetAddress.isReachable()уже происходит через порт 7, за исключением того, что последний более интеллектуален в отношении того, что IOExceptionsозначают различные возможные с точки зрения достижимости.
Marquis of Lorne
1
Да @EJP Я думаю, было бы здорово, если бы он InetAddress.isReachable()был перегружен, чтобы включить аргумент порта в стандартную библиотеку. Интересно, почему его не включили?
Сураб Бхат
7

Если вы хотите только проверить, подключен ли он к Интернету, используйте этот метод. Он возвращает true, если Интернет подключен. Предпочтительно, если вы используете адрес сайта, к которому вы пытаетесь подключиться через программу.

     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
источник
2
Бесполезно, если на сервере нет веб-сервера, это вообще не условие, указанное в вопросе.
Fran Marzoa
3

Просто упоминание об этом явно, поскольку другие ответы этого не делают. Для части ping isReachable () требуется root-доступ в Unix. И, как указывает bestsss в 4779367 :

И если вы спросите, почему ping из bash не работает, на самом деле он тоже нужен. Сделайте это ls -l / bin / ping.

Поскольку использование root не было вариантом в моем случае, решение заключалось в том, чтобы разрешить доступ к порту 7 в брандмауэре конкретному серверу, который меня интересовал.

Цитракс
источник
3

Я не уверен, в каком состоянии был задан первоначальный вопрос в 2012 году.

В настоящее время ping будет выполняться от имени пользователя root. После авторизации исполняемого файла ping вы увидите флаг + s и процесс, принадлежащий пользователю root, что означает, что он будет работать от имени пользователя root. запустите ls -liat, где находится пинг, и вы должны его увидеть.

Итак, если вы запустите InetAddress.getByName ("www.google.com"). IsReacheable (5000) как root, он должен вернуть true.

вам нужны надлежащие авторизации для сырого сокета, который используется ICMP (протокол, используемый ping)

InetAddress.getByName так же надежен, как ping, но вам нужны соответствующие разрешения для процесса, чтобы он работал правильно.

Клеберт Суконик
источник
0

Я бы предположил, что ЕДИНСТВЕННЫЙ надежный способ проверить подключение к Интернету - это фактически подключиться и загрузить файл, ИЛИ проанализировать вывод ping-вызова ОС через exec (). Вы не можете полагаться на код выхода для ping, а isReachable () - дерьмо.

Вы не можете полагаться на код выхода ping, поскольку он возвращает 0, если команда ping выполняется правильно. К сожалению, ping выполняется правильно, если он не может достичь целевого хоста, но получает сообщение «Целевой хост недоступен» от вашего домашнего ADSL-маршрутизатора. Это своего рода ответ, который обрабатывается как успешное попадание, поэтому код выхода = 0. Но нужно добавить, что это в системе Windows. Не проверял * никсы.

Cossoft
источник
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 - это количество попыток ping ip (пакетов), если у вас загруженная сеть или система выбирает большие числа nping.
wping - это время ожидания pong с ip, вы можете установить его на 2000 мс
для использования этого метода, вы можете написать это:

isReachable(5, 2000, "192.168.7.93");
Армин Мокри
источник
потенциальная инъекция команды, обязательно проверьте строковый ввод перед запуском
шпион
есть также сообщение об ошибке «Общий сбой». Это тоже похоже на Windows. Я почти уверен, что эти сообщения об ошибках локализованы на разных языках, поэтому их нельзя переносить.
шпион
это для систем Windows.
Армин Мокри
да, я видел это раньше на окнах
шпион
0

Или используя этот способ:

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 работает нестабильно и иногда возвращает недоступность для адресов, которые мы можем пропинговать.

Я пробовал следующее:

ping -c 1 <fqdn>и проверьте файл exit status.

Работает во всех случаях, которые я пробовал, где InetAddress.isReachableне работает.

Сандип Саркар
источник
-1

Поскольку вы можете проверить связь с компьютером, ваш Java-процесс должен работать с достаточными привилегиями для выполнения проверки. Вероятно, из-за использования портов нижнего диапазона. Если вы запустите свою java-программу с помощью sudo / superuser, я уверен, что она сработает.

Col
источник
Вам не нужны привилегии для подключения к портам в нижнем диапазоне.
Marquis of Lorne