我有点不高兴这不能以优雅的方式处理,在尝试了几个 SO 问题的答案中提到的不同解决方案( this , this 和其他几个)之后,我仍然无法检测到套接字断开连接(通过拔下电缆)。
我正在使用 NIO 非阻塞套接字,除了我找不到检测服务器断开连接的方法外,一切都运行良好。
我有以下代码:
while (true) {
handlePendingChanges();
int selectedNum = selector.select(3000);
if (selectedNum > 0) {
SelectionKey key = null;
try {
Iterator<SelectionKey> keyIterator = selector.selelctedKeys().iterator();
while (keyIterator.hasNext()) {
key = keyIterator.next();
if (!key.isValid())
continue;
System.out.println("key state: " + key.isReadable() + ", " + key.isWritable());
if (key.isConnectable()) {
finishConnection(key);
} else if (key.isReadable()) {
onRead(key);
} else if (key.isWritable()) {
onWrite(key);
}
}
} catch (Exception e) {
e.printStackTrace();
System.err.println("I am happy that I can catch some errors.");
} finally {
selector.selectedKeys().clear();
}
}
}
当正在读取 SocketChannels 时,我拔下电缆,Selector.select()
开始旋转并返回 0,现在我没有机会读取 或 < strong>write channels,因为主要的读写代码被if (selectedNum > 0)
守护着,这是我脑子里的第一个困惑,来自this answer , 据说当 channel 断开时,select() 会返回,并且 channel 的选择键会指示可读/可写,但显然这里不是这样,键不是选择后,select()
仍然返回 0。
此外,来自 EJP's answer类似的问题:
If the peer closes the socket:
- read() returns -1
- readLine() returns null
- readXXX() throws EOFException, for any other X.
这里也不是这种情况,我尝试注释掉 if (selectedNum > 0)
并使用 selector.keys().iterator()
无论如何获取所有键无论它们是否被选中,从这些键中读取都不会返回 -1(而是返回 0),写入这些键也不会抛出 EOFException
。我只注意到一件事,即使没有选择键,key.isReadable()
返回 true 而 key.isWritable()
返回 false(我猜这可能是因为我没有为 OP_WRITE 注册 key )。
我的问题是为什么 Java 套接字会这样,或者我做错了什么?
最佳答案
您已经发现您需要计时器和 TCP 连接上的心跳。
如果拔掉网线,TCP连接可能不会断开。如果你没有什么可发送的,TCP/IP 栈也没有什么可发送的,它不知道某处电缆不见了,或者对等 PC 突然起火了。在多年后重新启动服务器之前,可以认为该 TCP 连接是打开的。
这样想; TCP 连接如何知道另一端断开了网络 - 它断开了网络,所以它不能告诉你这个事实。
如果您拔下连接到服务器的电缆,有些系统可以检测到这一点,有些则不能。如果您拔下另一端的电缆,例如以太网交换机,不会被检测到。
这就是为什么一个 TCP 连接总是需要监督计时器(例如,向对等方发送心跳消息,或者在给定时间内没有 Activity 时关闭 TCP 连接),
一个非常便宜的方法可以至少避免 TCP 连接,您只从中读取数据,从不写入数据,并保持多年,就是在 TCP 套接字上启用 TCP keepalive - 请注意 TCP 的默认超时keepalive 通常是 2 小时。
关于java - 检测套接字断开?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14010194/