简体   繁体   中英

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

I have an non-blocking NIO server I wrote, for the most part it functions as expected with normal use; however I noticed that on occasion it sticks at 30% CPU usage for some reason. After a lot of time trying to force the bug I eventually triggered it when I disconnected the client right after the SocketChannel was accepted but before any bytes were read. This was purely by chance I caught it. I'd spent a good portion of the day trying to catch it and not managing.

It seems to happen once every few days or so. I've added the following code:

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

And my current server loop now looks like this:

    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);
    }

}

Will the addition of the if statement on the current key's channel catch sudden disconnects like the one's I'm occasionally getting? It's very rare that these occur and are not handled by OP_READ returning -1 on a read attempt, but they do seem to occur occasionally. will my fix work?

I've added the following code:

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

Pointless. isConnected() doesn't magically become false when the connection breaks. It only tells you whether you accepted or connected the channel, which you did. And if it did magically give you a connection status instead of just a channel status, you should close the channel. Not just cancel the key.

What you should put in its place is this:

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

and remove keyIterator.remove() from all the other locations. Possibly this may solve your problem. Otherwise you would have to post more of your code, specifically the code where you read and write from/to the channel, to show where your loop is. For example, if you catch an IOException doing either of these operations, you must close the channel concerned: it is broken.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM