java - 客户端突然与 Java NIO 断开连接后,服务器以 30% CPU 运行,我的修复是否有效?

标签 java nio nonblocking

我有一个我编写的非阻塞 NIO 服务器,在正常使用的情况下,它的大部分功能都按预期运行;但我注意到有时由于某种原因它的 CPU 使用率会停留在 30%。经过大量时间尝试强制执行该错误后,当我在接受 SocketChannel 后但在读取任何字节之前断开客户端连接时,最终触发了该错误。这纯粹是我偶然发现的。我花了一天的大部分时间试图捕获它,但没有成功。

似乎每隔几天左右就会发生一次。 我添加了以下代码:

 SocketChannel channel = (SocketChannel) key.channel();             
                    if(!channel.isConnected()){
                        key.cancel();
                        break;
                    }

我当前的服务器循环现在看起来像这样:

    try {

        Selector selector = Selector.open();
        serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.configureBlocking(false);
        ServerSocket serverSocket = serverSocketChannel.socket();
        serverSocket.bind(new InetSocketAddress(serverPort));

        log.info("Selector Thread: Server "
                + Server.SERVER_VERSION
                + " Runnable- Listening for connections on port: "
                + serverSocket.getLocalPort());

        running = true;

        @SuppressWarnings("unused")
        SelectionKey serverAcceptKey = serverSocketChannel.register(
                selector, SelectionKey.OP_ACCEPT);

        while (running) {
                    selector.select();
            Set<SelectionKey> selectedKeys = selector.selectedKeys();
            Iterator<SelectionKey> keyIterator = selectedKeys.iterator();

            while (keyIterator.hasNext()) {

                SelectionKey key = (SelectionKey) keyIterator.next();
                SocketChannel channel = (SocketChannel) key.channel();

                if(!channel.isConnected()){
                    key.cancel();
                    break;
                }
                if ((key.readyOps() & SelectionKey.OP_ACCEPT) == SelectionKey.OP_ACCEPT) {

                    acceptConnection(selector, key);
                    keyIterator.remove();

                } else if ((key.readyOps() & SelectionKey.OP_READ) == SelectionKey.OP_READ) {

                    readFromSocketChannel(key);
                    keyIterator.remove();

                } else if ((key.readyOps() & SelectionKey.OP_WRITE) == SelectionKey.OP_WRITE) {

                    writeToSocketChannel(key);
                    keyIterator.remove();
                }

            }

        }

    } catch (IOException e) {
        log.fatal(
                "An IOException occurred, Selector or ServerSocket could not open, shutting down...",
                e);
        System.exit(-1);
    }

}

在当前键的 channel 上添加 if 语句是否会捕获像我偶尔遇到的那样的突然断开连接? 这些情况很少发生,并且不会由 OP_READ 在读取尝试时返回 -1 来处理,但它们似乎偶尔会发生。我的修复会起作用吗?

最佳答案

I've added the following code:

SocketChannel channel = (SocketChannel) key.channel();             
if(!channel.isConnected()){
    key.cancel();
    break;
}

毫无意义。当连接中断时,isConnected() 不会神奇地变为 false。它只会告诉您是否接受或连接了该 channel (您确实这样做了)。如果它确实神奇地为您提供了连接状态而不仅仅是 channel 状态,则您应该关闭该 channel 。不仅仅是取消 key 。

您应该将其替换为:

keyIterator.remove(); // unconditionally
if (!key.isValid())
    continue;    // key has been cancelled

并从所有其他位置删除keyIterator.remove()。也许这可以解决您的问题。否则,您将不得不发布更多代码,特别是从 channel 读取和写入 channel 的代码,以显示循环的位置。例如,如果您在执行任一操作时捕获 IOException,则必须关闭相关 channel :它已损坏。

关于java - 客户端突然与 Java NIO 断开连接后,服务器以 30% CPU 运行,我的修复是否有效?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30522321/

相关文章:

java - 在给定日期的几个日期中查找最近的日期

java - 理解字段访问表达式

java - 如何在 JFace LabelProviders 中处理 Windows shell 图标?

javascript - React - 如何从长时间运行的操作将正确的 prop 传递给子组件

python - 在python中轮询键盘(检测按键)

java - 为什么在被 IOException 覆盖时使用 FileNotFoundException

java - C++ 客户端连接到非阻塞 Java NIO 服务器

java - 具有 AsynchronousServerSocketChannel 的多线程服务器

java - 使用FileSystemProvider实现FTP文件系统

multithreading - 非阻塞 I/O 真的比多线程阻塞 I/O 更快吗?如何?