Java 快速检查网络连接

11
我的问题很简单。我的程序需要在网络连接丢失时立即得到通知。我正在使用Java 5,所以无法使用非常方便的NetworkInterface功能。
目前我有两种不同的检查网络连接的方法:
(去除了try..catch)

方法一:

URL url = new URL("http://www.google.com");
HttpURLConnection urlConnect = (HttpURLConnection) url.openConnection();
// trying to retrieve data from the source. If offline, this line will fail:
Object objData = urlConnect.getContent();
return true;

方法二:

Socket socket = new Socket("www.google.com", 80);
netAccess = socket.isConnected();
socket.close();
return netAccess;

然而,这两种方法在返回false之前都会阻塞直到达到超时时间。我需要一种能够立即返回的方法,但要与Java 5兼容。

谢谢!

编辑:我忘记提到我的程序依赖于IP地址,而不是DNS名称。 因此,在解析主机时检查错误已经无效了...

第二次编辑:

使用NetworkInterface找到了最终的解决方案:

Enumeration<NetworkInterface> eni = NetworkInterface.getNetworkInterfaces();
        while(eni.hasMoreElements()) {
            Enumeration<InetAddress> eia = eni.nextElement().getInetAddresses();
            while(eia.hasMoreElements()) {
                InetAddress ia = eia.nextElement();
                if (!ia.isAnyLocalAddress() && !ia.isLoopbackAddress() && !ia.isSiteLocalAddress()) {
                    if (!ia.getHostName().equals(ia.getHostAddress()))
                        return true;
                }
            }
        }

我对Java不是很熟悉,但我认为方法2中的http://有点多余。 - glglgl
你可以使用Java 5.0来运行一个可以使用NetworkInterface的Java 6或7应用程序。 ;) - Peter Lawrey
似乎你需要一个回调方法来在网络状态发生变化时通知你。 - user195488
哎呀,那个实际上不在生产代码中...我会将其删除。 - BenCole
@CodeMonkey,这就是我要做的...我为此任务单独创建了一个线程。 - BenCole
2
我有点困惑你试图做什么。无论你如何检查连接是否仍然可用,你都需要通过网络进行通信,数据在网络上传输需要一些时间。我不明白任何人如何能够“立即”知道连接是否仍然活动。你确实需要一个超时时间。 - toto2
4个回答

10

自Java 5开始,InetAddress类中有一个isReachable()方法。您可以指定超时时间或要使用的NetworkInterface。关于ping使用的底层Internet控制消息协议(ICMP)的更多信息,请参见RFC 792 (http://www.faqs.org/rfcs/rfc792.html)。

Java2s中的用法示例:

  int timeout = 2000;
  InetAddress[] addresses = InetAddress.getAllByName("www.google.com");
  for (InetAddress address : addresses) {
    if (address.isReachable(timeout))
      System.out.printf("%s is reachable%n", address);
    else
      System.out.printf("%s could not be contacted%n", address);
  }

如果您想避免阻塞,请使用Java NIO(非阻塞I/O)中的java.nio包。

String host = ...; 
InetSocketAddress socketAddress = new InetSocketAddress(host, 80); 
channel = SocketChannel.open(); 
channel.configureBlocking(false); 
channel.connect(socketAddress);

不幸的是,这种方法仍然会阻塞,直到超时。更糟糕的是,它会为数组中的每个InetAddress阻塞。谢谢。 - BenCole
我对NIO不是很熟悉,但InetSocketAddress有一个带有InetAddress参数的构造函数,这是否有帮助?InetSocketAddress(InetAddress addr, int port) - Ali
我认为问题更多地出现在InetAddress中。它接受字节数组或字符串。如果字符串是IP地址,它只检查格式(即四个完整的八位组),而不检查有效性。[链接]http://download.oracle.com/javase/1.4.2/docs/api/java/net/InetAddress.html#getByAddress(java.lang.String, byte[]) - BenCole
这比我想到的任何主意都好。我会用它来进行服务器状态检查,并尝试解析主机名以进行一般连接检查。 - BenCole
仅适用于互联网连接。问题是网络连接的更改,而不是与谷歌的连接性。 - spy
显示剩余5条评论

4

首先,您需要创建一个名为 NetworkListener interface ,例如

public interface NetworkListener {
    public void sendNetworkStatus(String status);
}

接下来,创建一个类并称其为NetworkStatusThread,例如。
public class NetworkStatusThread implements Runnable {
    List listenerList = new Vector();

    @SuppressWarnings("unchecked")
    public synchronized void addNetworkListener(NetworkListener nl) {
        listenerList.add(nl);
    }

    public synchronized void removeNetworkListener(NetworkListener nl) {
        listenerList.remove(nl);
    }


    private synchronized void sendNetworkStatus(String status) {
        // send it to subscribers
        @SuppressWarnings("rawtypes")
        ListIterator iterator = listenerList.listIterator();
        while (iterator.hasNext()) {
            NetworkListener rl = (NetworkListener)iterator.next();
            rl.sendNetworkStatus(status);
        }
    }

    public void run() {
        while (true) {
        System.out.println("Getting resource status");
        try {
             Thread.sleep(2000);
             System.out.println("Sending resource status to registered listeners");
             this.sendResourceStatus("OK");
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

 }

然后在实例化Thread的类中,进行以下操作:

NetworkStatusThread netStatus = new NetworkStatusThread();
netStatus.addNetworkListener(this);
Thread t = new Thread(netStatus);
t.Start();

此外,在该类中,您需要实现NetworkListener以获取回调。

在上面的run()方法中,您可以实现@Ali编写的代码,但要传递我的状态:

  int timeout = 2000;
  InetAddress[] addresses = InetAddress.getAllByName("www.google.com");
  for (InetAddress address : addresses) {
    if (address.isReachable(timeout))
       this.sendResourceStatus("OK");
    else
       this.sendResourceStatus("BAD");
  }

谢谢!我之前一直在使用Timer和TimerTask的匿名子类,但我会尝试这个方法。 - BenCole
仅适用于联网连接。问题是网络连接的更改,而不是与谷歌的连接性。 - spy

3
我的程序需要在网络连接丢失时立即通知。
很遗憾,使用TCP/IP无法立即获得通知。它充满了缓冲、重试和超时,因此它不会立即执行任何操作,而TCP本身也没有任何与“拨号音”或任何API相对应的内容。唯一检测到丢失连接的方法是尝试通过它进行I/O操作。
我正在使用Java 5,因此无法使用非常方便的NetworkInterface功能。
它们也无法帮助您。它们只能告诉您NIC是启动还是关闭,无法提供有关您与更广泛世界的连接状态的信息。
URL url = new URL("http://www.google.com");
HttpURLConnection urlConnect = (HttpURLConnection) url.openConnection();
// trying to retrieve data from the source. If offline, this line will fail:
Object objData = urlConnect.getContent();
return true;

如果您离线,那么这个操作将由于 DNS 失败而超时。
Socket socket = new Socket("www.google.com", 80);
netAccess = socket.isConnected();
socket.close();
return netAccess;

同意。但是即使它没有连接,socket.isConnected()也总是返回true。这些API(如isConnected()、isBound()、isClosed())只告诉您您对socket做了什么。它们不会告诉您连接的状态。因为上述原因,它们不能。
如果出现异常,您忘记关闭socket,因此您存在资源泄漏问题。
“我需要一个能立即返回的方法。” 这是不可能的,出于上述原因,你需要重新设计实现这个函数的方式。

实际上,我能够使用NetworkInterface的功能来解决这个问题(我想)。请查看上面的第二次编辑。另外,我不知道你是否注意到,在调用isConnected()之后,我会立即调用socket.close()... - BenCole
在异常情况下,您忘记关闭套接字。 - user207421

0

你尝试过通过发出DNS解析吗?

你可以尝试使用以下方法:

public static InetAddress[] getAllByName(String host)

但我不确定这是否也需要超时。


这个功能通过抛出UnknownHostException来实现,但仅当主机不是IP地址时才会抛出。 - BenCole

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