Java检测丢失连接

28

当我使用PuTTY等工具时,如果我的连接丢失(或者我在Windows上手动使用ipconfig /release命令),它会直接响应并通知我连接丢失了。

我想创建一个Java程序来监视我的Internet连接(到一些可靠的服务器),以记录当我的互联网连接失败的日期/时间。

我尝试使用Socket.isConnected()方法,但它将永远返回“true”。 我该如何在Java中实现这个功能?

9个回答

23

判断连接是否中断的最佳方法是尝试读取/写入套接字。如果操作失败,则说明连接已经中断。

因此,您只需要在一定时间间隔内尝试读取,如果读取失败则尝试重新连接。

对于您来说,重要的事件将是读取失败 - 您失去了连接,以及连接新套接字时 - 您重新获得了连接。

这样您就可以跟踪上线时间和下线时间。


2
不正确。如果您设置了读取超时并触发了异常,则操作将失败,但这并不一定表示连接丢失。 - user207421
3
@EJP这句话并不总是准确的,但它可以让你对正在发生的事情有一个很好的概念。 - jjnguy
12
不会的。你无法区分慢服务器和读取超时导致的失去连接。 - user207421

14
尽管TCP/IP是“面向连接”的协议,但通常在空闲连接上不发送任何数据。通过IP堆栈,您可以打开一个套接字一年,而没有一个比特被发送到它上面。为了注意到连接丢失,您必须在应用程序级别上发送一些数据。(*) 您可以通过从ADSL调制解调器中拔出电话线来尝试此操作。除非应用程序具有某种应用程序级别的keepalive机制,否则您的PC中的所有连接都应保持稳定。
因此,通知断开连接的唯一方法是打开到某个服务器的TCP连接并从中读取一些数据。也许最简单的方法可能是连接到某个FTP服务器并定期获取一个小文件或目录列表。我从未见过真正旨在用于此情况的通用服务器,而FTP服务器的所有者可能不喜欢客户端这样做。
(*) 还有一种称为TCP keepalive的机制,但在许多操作系统中,您必须为所有应用程序激活它,并且如果您想快速注意到连接丢失,则实际上不太实用。

8
如果客户端正常断开连接,read() 将返回 -1,readLine() 返回 null,对于任何其他 X 的 readXXX() 都会抛出 EOFException。检测丢失的 TCP 连接的唯一可靠方法是向其写入数据。最终这将抛出一个 IOException 'connection reset',但由于缓冲区,至少需要写入两次才能抛出此异常。

7

为什么不使用java.net.InetAddress类的isReachable()方法?

这个方法的工作原理取决于JVM的实现,但是:

典型的实现会使用ICMP ECHO REQUEST(如果可以获得特权),否则它将尝试在目标主机的7号端口上建立TCP连接(回显)。

如果您想保持一个持续开放的连接,以便在连接失败时可以看到它,您可以自己连接运行ECHO协议的服务器,而不是让isReachable()为您执行此操作,并读取和写入数据并等待它失败。


1
嗯,也许我的问题没有表述清楚。在我的系统中有时会发生互联网断开连接几秒钟并关闭所有连接的情况。一秒钟后一切都正常了。我想每天监控这种情况,以制作报告,了解它发生的频率。 - kresjer
你可以自己连接到ECHO服务器并不断发送和读取数据;将此添加到答案中。 - David Webb
2
现有连接是否仍然打开与isReachable()完全无关。 - user207421

1

好的,我终于让它与之配合工作了。

try
{
    Socket s = new Socket("stackoverflow.com",80);
    DataOutputStream os = new DataOutputStream(s.getOutputStream());
    DataInputStream is = new DataInputStream(s.getInputStream());
    while (true)
    {
        os.writeBytes("GET /index.html HTTP/1.0\n\n");
        is.available();
        Thread.sleep(1000);
    }
}
catch (IOException e)
{
    System.out.println("connection probably lost");
    e.printStackTrace();
}

不如我希望的那么干净,但如果我省略os.writeBytes(),它就无法工作。


19
这是一样绝对不应该使用的东西。它几乎可以被视为针对stackoverflow.com的DOS攻击! - tputkonen
一个更友好的方法是在网络主机上托管一个简单的页面/脚本,这样就不会对服务器造成太大的负载,特别是如果渲染的页面很小(只需要一个简单的“h”即可)。这应该提供一个小的足迹,如果每秒钟进行一次调用,服务器可能不会崩溃导致DOS攻击。至少在理论上是这样的。 - Kingsolmn
4
这是无意义的。调用available()通常毫无意义,而调用它并丢弃结果则更是如此。睡眠时间纯粹浪费时间。这种技术实际上什么也没做。 - user207421

1

你可能想尝试查看套接字超时间隔。使用短暂的超时时间(我相信默认值为“无限超时”),当主机变得不可达时,您可能能够捕获异常或其他错误。


1
这并不能检测所有情况。你需要读取或者写入socket才行。 - tputkonen

0

Socket.java类中的isConnected()方法有点误导人。它并不告诉你套接字当前是否连接到远程主机(比如是否未关闭)。相反,它告诉你套接字是否曾经连接到远程主机。如果套接字能够连接到远程主机,即使该套接字被关闭后,此方法也会返回true。要判断套接字当前是否打开,您需要检查isConnected()返回trueisClosed()返回false

boolean connected = socket.isConnected() && !socket.isClosed();

1
要判断套接字是否处于打开状态,您需要检查isConnected()返回true并且isClosed()返回false。但这似乎并不正确。请参见:[https://dev59.com/3KLia4cB1Zd3GeqPostR](链接) - user316117
@user316117,那个问题不够清晰,但如果isClosed为FALSE且isConnected为TRUE,则意味着您已经成功连接并且连接从未关闭。如果您认为自己已断开连接,并且“isClosed()”返回FALSE,则表示您发现了JDK错误。请参阅此文档:https://docs.oracle.com/javase/8/docs/api/java/net/Socket.html#isConnected-- - Alboz
1
@Alboz 这意味着您已成功连接,socket 尚未关闭。这与连接无关,正如您在自己的答案中所述。除非您关闭了自己的 socket 并且然后得到 isClosed() == false,否则没有 JDK bug。 - user207421
@Alboz 如果套接字与另一端断开连接,isClosed() 仍将为 false,而 isConnected 将为 true。我认为检查它的最佳方法是尝试向管道写入数据,这就是我正在做的事情。 - justcode

0

可能更简单的方法是连接到雅虎/谷歌或类似的地方。

    URL yahoo = new URL("http://www.yahoo.com/");
    URLConnection yc = yahoo.openConnection();
    int dataLen = yc.getContentLength() ;

尼尔


与现有连接是否仍然打开没有任何关系。 - user207421
问题是如何编写一个“监控我的互联网连接的Java程序”,这个程序可以很好地完成这个任务。Java本身无法监控putty连接是否丢失,但会在发生通用网络问题时发出警告。您可以从Java中调用“netstat”来监视另一个进程的网络连接。 - Neil Wightman
使用WinSock的Windows程序员可以注册事件。即使电缆已断开,此事件仍会触发(延迟几秒钟)。[https://stackoverflow.com/questions/44681648/in-android-how-can-i-receive-an-event-when-my-socket-closes-or-disconnects](链接) - user316117

0
你可以每隔几秒钟ping一次机器,这样会相当准确。但要小心不要DOS攻击它。
另一个选择是在远程机器上运行一个小型服务器并保持连接。

你怎么知道自己已经从小型服务器断开连接了呢?Java 的一个主要用途是在 Android 移动设备上,而持续的 ping 和 keepalive 将会消耗电池寿命,因此这是不明智的选择。 - user316117

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