如何测试远程系统是否可达?

24
我想使用Java测试远程系统是否可达,或者用其他话说,用Java“发送ping”。该功能应封装在一个返回布尔值的方法中,例如:
public boolean isReachable(String ip) {
   // what to do :-)
}

我测试了Java Process类,但我认为这不是最好的方法,因为需要处理输出缓冲区,过程相对复杂。

Process proc = Runtime.getRuntime().exec("ping " + ip);

另一种可能性是创建一个 Socket 连接并处理抛出的异常,但如果远程系统是一个“裸”Unix系统,则可能在另一侧没有 Socket :-) 另外,当远程系统无法访问时,我想设置超时时间。那么,我该怎么做呢?谢谢!

为了什么目的?如果有一个服务器你想连接,就尝试连接。 - user207421
5个回答

55
InetAddress.getByName(ip).isReachable(timeout);

10
我们可以检查特定端口是否可达。 - tasomaniac
1
此函数不应在生产设置中使用,如果您无法完全控制特权、防火墙等,请勿使用。请参见:https://dev59.com/E2kw5IYBdhLWcg3wZJt9 - ssindelar
好看,简单,干净。 - xdevs23

12

InetAddress.getByName(host).isReachable(timeOut) 用于检测主机是否可达,详情请见此处


7
isReachable() 不可靠地用于 Ping 当地子网以外的主机。最有可能的原因是 Windows 使用 ICMP ping,而 isReachable() 未能使用它。直到至少 Java 6,这种情况仍然存在。 - flash
5
Inet4AddressImpl.c 中本地的 isReachable0 方法实现中有一条注释:“目前Windows ICMP和RAW套接字的实现过于不可靠。因此最好根本不要尝试使用它,只依赖于TCP。我们可能会在未来重新审视并启用这段代码。” - Mister Smith
1
已确认,在我的WXP测试机上,该命令默认使用端口7(回显)进行TCP连接。它即使针对不存在的IP地址或被防火墙阻挡的地址,也会返回可达状态。 - Mister Smith

5

看起来你正在使用Linux,所以你可能会发现isReachable()不可靠(因为你没有权限发送ICMP数据包,并且很少有服务器运行Echo服务)。

如果是这种情况,我认为你需要像你建议的那样生成一个进程,但我建议使用类似以下的命令:

   ping -c 1 hostname

这将在一次尝试后终止,然后您可以检查进程的退出状态 - 这比解析标准输出更可靠。

Ping成功返回0,失败返回非零值。


非常感谢您的建议!我会在漏洞跟踪器中创建一个“改进”条目 :-) - strauberry

2
根据我的经验,没有一种100%可靠的方法来做到这一点,你必须通过尝试多个选项或将它们结合起来来选择,但是isReachable()不能作为可靠的选项。我认为更好的纯Java选项可能是这样的:
private static boolean isReachable(String host, int openPort, int timeOutMillis) {
    try {
        try (Socket soc = new Socket()) {
            soc.connect(new InetSocketAddress(host, openPort), timeOutMillis);
        }
        return true;
    } catch (IOException ex) {
        return false;
    }
}

如果你想检查主机是否可以通过网络/浏览器访问,这是它的地址:
private static boolean hostsWebApp(String host, int timeOutMillis) {
    boolean isReachable = isReachable(host, 80, timeOutMillis);
    if(!isReachable) {
        return isReachable(host, 443, timeOutMillis);
    } else {
        return true;
    }
}

更好的选择是,请添加soc.close()。但仍然不能提供一致的结果。有些主机只能在多次重试后才能访问。 - muratozyurt
1
由于在 try-with-resource 语句中,不需要关闭。 - sedrakpc
你说得对。我的问题不同,涉及到多个线程,网络需要多次重试 soc.connect()。谢谢。 - muratozyurt

1

我知道这个问题已经有了答案,但是我想添加我的代码以便"复制-粘贴"。

public boolean isIpReachable(String ip, int timeout){
    boolean state = false;

    try {
        state = InetAddress.getByName(ip).isReachable(timeout);
    } catch (IOException e) {
        //Parse error here
    }

    return state;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接