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

Server running at 30% CPU after sudden client disconnect with Java NIO, will my fix work?

我有一个自己编写的非阻塞 NIO 服务器,在大多数情况下,它在正常使用时都能按预期运行;但是我注意到有时出于某种原因它会停留在 30% CPU 的使用率。经过大量时间尝试强制执行该错误后,我在 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);
    }

}

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

I've added the following code:

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

毫无意义。 isConnected() 不会在连接中断时神奇地变为 false。它只会告诉您您是否接受或连接了您所做的频道。如果它确实神奇地为您提供了连接状态而不仅仅是频道状态,您应该关闭频道。不只是取消密钥。

你应该放在它的位置是这样的:

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

并从所有其他位置删除 keyIterator.remove()。可能这可以解决您的问题。否则你将不得不 post 更多你的代码,特别是你读写 from/to 频道的代码,以显示你的循环在哪里。例如,如果你发现一个 IOException 正在做这些操作中的任何一个,你必须关闭相关的频道:它坏了。