【问题标题】:Why does InetAddress.isReachable return false, when I can ping the IP address?当我可以 ping IP 地址时,为什么 InetAddress.isReachable 返回 false?
【发布时间】:2012-04-12 22:22:57
【问题描述】:
InetAddress byName = InetAddress.getByName("173.39.161.140");
System.out.println(byName);
System.out.println(byName.isReachable(1000));

为什么isReachable 返回false?我可以ping通IP。

【问题讨论】:

  • 类似。但我找不到任何解决问题的线索。所以我在这里重新提出了它。感谢您的提醒!
  • 我会尝试增加超时时间。
  • 这是一个很好的问题,没有足够的支持。我发现的唯一类似问题被标记为 clojure 问题,答案没有定论。
  • 有人能告诉我 isReachable() 到底是做什么的吗?即使在本地主机上它也会返回错误...

标签: java inetaddress


【解决方案1】:

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

ping 的 -c 选项将允许 ping 简单地尝试到达服务器一次(而不是我们习惯在终端使用的无限 ping)。

如果主机可访问,这将返回 0。否则,您将获得“2”作为返回值。

简单得多 - 但当然它是特定于平台的。 使用此命令可能有某些特权警告 - 但我发现它适用于我的机器。


请注意: 1) 此解决方案不是生产质量。它有点像黑客。如果谷歌关闭,或者您的互联网暂时变慢,或者即使您的权限/系统设置中有一些有趣的东西,如果可能会返回误报(即,即使输入地址可以访问,它也可能会失败)。 2) isReachable 失败是一个突出的问题。再次 - 有几个在线资源表明在撰写本文时没有“完美”的方式来执行此操作,因为 JVM 尝试访问主机的方式 - 我想这是一个本质上特定于平台的任务,虽然很简单, 还没有被 JVM 充分抽象出来。

【讨论】:

  • @jayunit100 对于这两种方法都不起作用,isReachable 我失败了并且使用 ping 我得到了 icmp 不允许?现在知道怎么处理了吗?
  • @Yuvi 如果您使用的是 Windows,则标志会有所不同。而不是 -c 你想要 -n.
  • waitFor() 需要 10 秒才能返回。有没有办法在 Java 7 中提及超时?
  • @Jaydev,还有一个重载选项:public boolean waitFor(long timeout, TimeUnit unit) in java.lang.Process (@since 1.8)
  • 截至 2020 年 1 月,我可以毫无问题地运行该问题的示例,它报告一个 IPv4 和一个 IPv6 地址的“已连接”。 bug 也在 Java 5.0 b22 中得到修复。也许isReachable 现在更可靠了。
【解决方案2】:

我来这里是为了获得同样问题的答案,但我对任何答案都不满意,因为我正在寻找一个独立于平台的解决方案。这是我编写的代码,它独立于平台,但需要有关另一台机器上任何开放端口的信息(我们大部分时间都有)。

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 的想法很好:)
  • 所以,只是为了确保我完全理解:只要没有异常,地址就可以到达?
  • 这与 InetAddress.isReachable() 已经通过端口 7 所做的相同,只是后者更智能地了解各种可能的 IOExceptions 在可达性方面的含义。
  • 是的@EJP 我认为如果InetAddress.isReachable() 被重载以在标准库中包含端口参数会很棒。我想知道为什么不包括在内?
【解决方案3】:

如果你只想检查它是否连接到互联网使用此方法,如果互联网连接它返回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;
    }

【讨论】:

  • 如果服务器没有网络服务器就没有用了,这根本不是问题中指定的条件。
【解决方案4】:

只是明确提及它,因为其他答案没有。 isReachable() 的 ping 部分需要 Unix 上的 root 访问权限。正如4779367中的bestsss所指出的那样:

如果你问为什么从 bash ping 不需要,实际上它也需要。这样做 ls -l /bin/ping。

由于在我的情况下使用 root 不是一个选项,因此解决方案是允许访问防火墙中的端口 7 到我感兴趣的特定服务器。

【讨论】:

    【解决方案5】:

    我不确定 2012 年最初的问题被问到时的状态是什么。

    就目前而言,ping 将作为 root 执行。通过 ping 可执行文件的授权,您将看到 +s 标志,以及属于 root 的进程,这意味着它将以 root 身份运行。在 ping 所在的位置运行 ls -liat,您应该会看到它。

    因此,如果您以 root 身份运行 InetAddress.getByName("www.google.com").isReachable(5000),它应该返回 true。

    您需要对 ICMP(ping 使用的协议)使用的原始套接字进行适当的授权

    InetAddress.getByName 与 ping 一样可靠,但您需要对进程具有适当的权限才能使其正常运行。

    【讨论】:

      【解决方案6】:

      由于您可以 ping 计算机,因此您的 Java 进程应该以足够的权限运行以执行检查。可能是由于使用了较低范围内的端口。如果你使用 sudo/superuser 运行你的 java 程序,我敢打赌它会工作。

      【讨论】:

      • 您不需要特权即可连接到低范围的端口。
      【解决方案7】:

      我建议测试互联网连接的唯一可靠方法是实际连接并下载文件,或者通过 exec() 解析 OS ping 调用的输出。您不能依赖 ping 的退出代码,并且 isReachable() 是废话。

      您不能依赖 ping 退出代码,因为如果 ping 命令正确执行,它会返回 0。不幸的是,如果 ping 无法到达目标主机但从您的家庭 ADSL 路由器获得“目标主机无法访问”,则 ping 会正确执行。这是一种被视为成功命中的回复,因此退出代码 = 0。尽管这是在 Windows 系统上,但必须添加。未检查 *nixes。

      【讨论】:

        【解决方案8】:
         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 是等待来自 ip 的 pong 的时间,您可以将其设置为 2000ms
        对于使用这种方法,你可以这样写:

        isReachable(5, 2000, "192.168.7.93");
        

        【讨论】:

        • 潜在的命令注入,运行前一定要验证输入的字符串
        • 还有“一般故障”错误消息。这也看起来像特定于 Windows 的。我很确定这些错误消息已本地化为不同的语言,因此不是超级便携。
        • 这是针对windows系统的。
        • 是的,我以前在 Windows 上看到过
        【解决方案9】:

        或者使用这种方式:

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

        【讨论】:

          【解决方案10】:

          InetAddress.isReachable 不稳定,有时会返回无法访问的地址,我们可以 ping。

          我尝试了以下方法:

          ping -c 1 &lt;fqdn&gt; 并检查exit status。

          适用于我尝试过的所有 InetAddress.isReachable 不起作用的情况。

          【讨论】:

            【解决方案11】:

            检查互联网

            public boolean isInternetAvailable() {
                    try {
                        InetAddress ipAddr = InetAddress.getByName("google.com");
                        //You can replace it with your name
                        return !ipAddr.equals("");
            
                    } catch (Exception e1) {
                        try {
                            Process p1 = java.lang.Runtime.getRuntime().exec("/system/bin/ping  -W 1 -c 1 www.google.com");
                            int returnVal = 0;
                            returnVal = p1.waitFor();
                            boolean reachable = (returnVal==0);
                            return reachable;
                        } catch (Exception e2) {
                            e2.printStackTrace();
                            return false;
                        }
                    }
                }
            

            检查网络连接

            private boolean isNetworkConnected() {
                    ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
            
                    return cm.getActiveNetworkInfo() != null && cm.getActiveNetworkInfo().isConnected();
                }
            

            【讨论】:

              【解决方案12】:

              因为isReachable使用的是TCP协议(by WireShark) Ping命令使用的是ICMP协议,如果要返回true需要打开7端口

              【讨论】:

              • * Android 实现首先尝试 ICMP ECHO REQUEST,如果失败,它将回退到 TCP ECHO REQUEST。任一协议的成功都将 * 返回 true。
              猜你喜欢
              • 2013-06-03
              • 1970-01-01
              • 2021-05-23
              • 2011-05-22
              • 1970-01-01
              • 2018-03-09
              • 1970-01-01
              • 2012-05-21
              • 2017-07-30
              相关资源
              最近更新 更多