正在检测插座断开连接?

2022-09-03 00:20:01

我有点沮丧,这不能以优雅的方式处理,在尝试了不同的解决方案(这个这个和其他几个)在几个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();
        }
    }
}

在读取套接字通道时,我拔下电缆,开始旋转并返回0,现在我没有机会读取写入通道,因为主要的读取和写入代码被保护,现在这是我脑海中出现的第一个混乱,从这个答案来看,据说当通道断开时, select() 将返回,通道的选择键将指示可读/可写,但显然不是这种情况,键未被选中,仍然返回 0。Selector.select()if (selectedNum > 0)select()

此外,从EJP对类似问题的回答中可以看出:

如果对等体关闭套接字:

  • read() 返回 -1
  • readLine() 返回空值
  • readXXX() 为任何其他 X 抛出 EOFException。

这里的情况也不是这样,我尝试注释掉并用于获取所有键,无论它们是否被选中,从这些键读取不会返回-1(返回0),并且写入这些键不会被抛出。我只注意到一件事,即使没有选择密钥,返回true而返回false(我想这可能是因为我没有为OP_WRITE注册密钥)。if (selectedNum > 0)selector.keys().iterator()EOFExceptionkey.isReadable()key.isWritable()

我的问题是为什么Java套接字表现得像这样,或者我做错了什么?


答案 1

您已经发现您需要计时器和 TCP 连接上的检测信号。

如果拔下网线,TCP 连接可能不会断开。如果您没有要发送的内容,则TCP / IP堆栈没有要发送的内容,它不知道电缆是否在某个地方消失了,或者对等PC突然爆发了火焰。该 TCP 连接可能被视为打开,直到几年后重新启动服务器。

这样想吧;TCP连接如何知道另一端从网络中消失了 - 它离开了网络,所以它不能告诉你这个事实。

如果您拔下进入服务器的电缆,某些系统可以检测到这种情况,而有些系统则不会。如果拔下以太网交换机另一端的电缆,则不会检测到。

这就是为什么人们总是需要主管计时器(例如,向对等体发送检测信号消息,或者在给定时间内没有活动的情况下关闭TCP连接)进行TCP连接,

一种非常便宜的方法,至少可以避免您只从中读取数据,从不写入的TCP连接,以连续多年保持运行,方法是在TCP套接字上启用TCP保持活动状态 - 请注意,TCP保持活动的默认超时通常为2小时。


答案 2

这些答案都不适用。第一个涉及连接断开的情况,第二个(我的)涉及对等体关闭连接的情况。

在TCP连接中,除非正在发送或接收数据,否则原则上没有任何关于拉动电缆会断开连接的内容,因为TCP被故意设计为在这种事情上具有健壮性,并且肯定没有任何东西可以像对等关闭那样在本地应用程序上查找。

在 TCP 中检测断开连接的唯一方法是尝试通过它发送数据,或者在适当的时间间隔后将读取超时解释为丢失的连接,这是应用程序决策。

您还可以将 TCP 保持活动状态设置为打开以启用对断开的连接的检测,在某些系统中,您甚至可以控制每个套接字的超时。但是,不是通过Java,因此您仍然坚持使用系统默认值,除非对其进行修改,否则应该是两个小时。

你的代码应该在调用 keyIterator.next() 之后调用 keyIterator.remove()。


推荐