Чому 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 ()? це повертає мене помилково навіть на localhost ...
Сет Кено,

Відповіді:


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 просто спробувати дістатися до сервера один раз (на відміну від нескінченного пінгу, який ми звикли використовувати на терміналі).

Це поверне 0, якщо хост доступний . В іншому випадку ви отримаєте "2" як повернене значення.

Набагато простіше - але, звичайно, це залежить від платформи. І можуть бути певні застереження щодо використання цієї команди, але я вважаю, що це працює на моїх машинах.


ВАЖЛИВО, зверніть увагу, що: 1) Це рішення не є якістю виробництва. Це трохи хак. Якщо Google не працює, або ваш Інтернет тимчасово працює повільно, або, можливо, навіть якщо у ваших привілеях / налаштуваннях системи є якась забава, якщо це може повернути помилкові негативи (тобто це може не вдатися, навіть якщо вхідна адреса доступна). 2) Помилка isReachable - невирішена проблема. Знову ж таки - є кілька Інтернет-ресурсів, які вказують на те, що на момент написання цієї статті не існує "ідеального" способу зробити це через те, як JVM намагається зв’язатися з хостами - я думаю, це внутрішньо специфічне завдання, яке, хоча і просте , ще не було достатньо абстраговано JVM.


@ jayunit100, жоден з підходів не працює, якщо isReachableя отримую помилку і використовую ping, я отримую icmp не дозволено? Чи знаєте ви, як із цим боротися зараз?
Юві

6
@Yuvi Якщо ви використовуєте Windows, прапори відрізняються. Замість -c ти хочеш -n.
James T Snell,

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
Я щасливий, що опинився тут, хороший спосіб думати Sourabh :)
JustADev

Тож, щоб переконатись, що я повністю розумію: поки немає винятку, адреса була доступною?
Timmiej93

1
Це ідентично тому, що InetAddress.isReachable()вже робиться через порт 7, за винятком того, що останній є більш розумним щодо того, що різні можливі IOExceptionsзначення з точки зору доступності.
Маркіз Лорнський

1
Так @EJP, я думаю, було б чудово, якби InetAddress.isReachable()перевантажити включення аргументу порту у стандартну бібліотеку. Цікаво, чому його не включили?
Sourabh Bhat

7

Якщо ви хочете лише перевірити, чи підключений він до Інтернету, використовуйте цей метод, він повертає істину, якщо підключений Інтернет. Бажано, якщо ви використовуєте адресу веб-сайту, який ви намагаєтеся підключити через програму.

     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
Марно, якщо на сервері немає веб-сервера, це взагалі не умова, зазначена у питанні.
Fran Marzoa

3

Просто прямо згадуючи це, оскільки інші відповіді цього не роблять. Пінг-частина isReachable () вимагає кореневого доступу в Unix. І як зазначив bestsss у 4779367 :

І якщо ви запитаєте, чому пінг з bash цього не робить, насправді це теж потрібно. Зробіть це ls -l / bin / ping.

Оскільки в моєму випадку використання root не було варіантом, рішення полягало в тому, щоб дозволити доступ до порту 7 у брандмауері до конкретного сервера, який мене цікавив.


3

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

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

Отже, якщо ви запустили InetAddress.getByName ("www.google.com"). IsReacheable (5000) як корінь, він повинен повернути true.

вам потрібні належні дозволи для вихідного сокета, який використовується ICMP (протокол, який використовує ping)

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


0

Я б припустив, що ЄДИНИЙ надійний спосіб перевірити підключення до Інтернету - це насправді підключитися І завантажити файл, АБО проаналізувати вихідні дані виклику пінгу ОС через exec (). Ви не можете покладатися на код виходу для ping, і isReachable () є глупством.

Ви не можете покладатися на код виходу ping, оскільки він повертає 0, якщо команда ping виконується правильно. На жаль, ping виконується правильно, якщо він не може досягти цільового хоста, але отримує "Кінцевий хост недоступний" від вашого домашнього маршрутизатора ADSL. Це свого роду відповідь, яка трактується як успішне потрапляння, таким чином, вихідний код = 0. Хоча потрібно додати, що це в системі Windows. Не перевірено * 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 - це кількість спроб пінгувати ip (пакети), якщо у вас зайнята мережа або системи, вибирайте більші числа nping.
wping - це час очікування на понг від ip, ви можете встановити йому 2000 мс
для використання цього методу. Ви можете написати це:

isReachable(5, 2000, "192.168.7.93");

потенційне введення команди, не забудьте перевірити введення рядка перед запуском цього
шпигун

є також повідомлення про помилку "загальна помилка". Це також схоже на вікна. Я майже впевнений, що ці повідомлення про помилки локалізовані різними мовами, тому не супер портативні.
шпигун

це для систем 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, я впевнений, що це працює.


Вам не потрібні привілеї для підключення до портів у низькому діапазоні.
Маркіз Лорнський,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.