java - 检测套接字断开?

标签 java sockets nio

我有点不高兴这不能以优雅的方式处理,在尝试了几个 SO 问题的答案中提到的不同解决方案( thisthis 和其他几个)之后,我仍然无法检测到套接字断开连接(通过拔下电缆)。

我正在使用 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/

相关文章:

java - 为什么我们要在一些递归算法中复制一个ArrayList?

java - 可以通过合并对可变对象的引用来公开内部表示

c# - 在 TcpClient 中设置 TCP_QUICKACK 选项

c - OpenSSL openwrt 套接字绑定(bind)失败

java - 避免 Java 8 Files.walk(..) 终止原因 ( java.nio.file.AccessDeniedException )

java.util.zip.ZipError : invalid CEN header (bad signature)

java - 从 Servlet 请求输入流读取时套接字超时

java - BeanCreationException : Error creating bean with name 'entityManagerFactory'

php - 如何识别客户端PHP Socket?

java - 无法在 sql 上添加或更新子行