提问人:neevek 提问时间:12/23/2012 最后编辑:Communityneevek 更新时间:5/9/2017 访问量:21657
检测插座断开?
Detecting socket disconnection?
问:
我有点沮丧,这不能以优雅的方式处理,在尝试了几个 SO 问题的答案中提到的不同解决方案(这个、这个和其他几个)之后,我仍然无法检测到插座断开(通过拔下电缆)。
我正在使用 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 时,我拔下电缆,开始旋转并返回 0,现在我没有机会读取或写入通道,因为主要的读写代码由 ,现在这是我脑海中出现的第一个困惑,从这个答案来看,据说当通道断开时, select() 将返回,通道的选择键将指示可读/可写,但这里显然不是这样,键没有被选中,仍然返回 0。Selector.select()
if (selectedNum > 0)
select()
此外,从 EJP 对类似问题的回答:
如果对等方关闭套接字:
- read() 返回 -1
- readLine() 返回 null
- readXXX() 抛出 EOFException,对于任何其他 X。
这里的情况也并非如此,我尝试注释掉并使用来获取所有键,无论它们是否被选中,从这些键中读取不会返回 -1(而是返回 0),并且写入这些键不会被抛出。我只注意到一件事,即使没有选择键,也会返回 true,而返回 false(我想这可能是因为我没有为 OP_WRITE 注册密钥)。if (selectedNum > 0)
selector.keys().iterator()
EOFException
key.isReadable()
key.isWritable()
我的问题是为什么 Java 套接字会表现得像这样,还是我做错了什么?
答:
这些答案都不适用。第一个涉及连接断开的情况,第二个(我的)涉及对等方关闭连接的情况。
在TCP连接中,除非正在发送或接收数据,否则原则上不会拉动电缆来中断连接,因为TCP被故意设计为在这种事情上是健壮的,当然也没有什么可以像对等关闭那样在本地应用程序上看到的。
在 TCP 中检测断开连接的唯一方法是尝试通过该连接发送数据,或者将读取超时解释为在适当的时间间隔后丢失的连接,这是应用程序的决定。
您还可以将 TCP 保持活动状态设置为打开以启用对断开连接的检测,在某些系统中,您甚至可以控制每个套接字的超时。但是,不是通过 Java,因此您只能使用系统默认值,除非对其进行修改,否则应该是两个小时。
您的代码应在调用 keyIterator.next() 后调用 keyIterator.remove()。
评论
keyIterator.remove()
selector.selectedKeys().clear()
您发现 TCP 连接上需要计时器和检测信号。
如果拔下网线,TCP 连接可能不会断开。如果没有什么可发送的,TCP/IP 堆栈就没有什么可发送的,它不知道电缆在某处消失了,或者对等 PC 突然起火了。该 TCP 连接可以被视为打开,直到您几年后重新启动服务器。
这样想吧;TCP连接怎么知道另一端脱离了网络 - 它已经脱离了网络,所以它不能告诉你这个事实。
如果您拔下进入服务器的电缆,某些系统可以检测到这一点,而有些系统则不会。如果您在以太网交换机的另一端拔下电缆,则不会检测到。
这就是为什么对于TCP连接,总是需要supervisor计时器(例如,向对等方发送心跳消息,或根据给定时间内没有活动关闭TCP连接)。
至少避免只读取数据,从不写入的 TCP 连接,连续多年保持正常运行的一种非常便宜的方法是在 TCP 套接字上启用 TCP keepalive - 请注意,TCP keepalive 的默认超时通常为 2 小时。
评论
select()