Java NIO SeletionKey 迭代器和键处理,我做对了吗?

Java NIO SeletionKey iterator and key handling, am I doing it right?

我正在关注 this tutorial on Java NIO. I found what appears to be the correct java file Here。我已经调整我的代码以在单个端口而不是多个端口上运行,并且只是将数据输出到屏幕而不是将其回显给客户端。

当我 运行 代码时,我的版本和上面 hyper-link 上的版本,似乎通道处理不当。我只是从我的笔记本电脑风扇的声音得出这个结论,在快速发送一些 System.out.println() 消息后,我设法缩小了问题的范围。起初我认为 selector.select() 方法没有因为某种原因阻塞,通道似乎是空的,但是 while(hasContent) 循环一直在迭代。

经过一番搜索,我找到了 this post,并意识到我可能没有正确处理密钥。从通道读取数据后,我调整了代码以取消 while(iterator.hasNext()) 循环底部 if 语句中的 SelectionKey。

这似乎已经成功了,现在我收到一条消息,通知我在关闭通道之前没有更多的字节要读取,而且总体上 运行 似乎更顺利。

这是我到目前为止所做的代码,我对如何实现它的想法是否正确?我是否在正确的位置取消了密钥?

public class ServerRunnable implements Runnable {

    private int serverPort;
    private int queueLength;

    public ServerRunnable(int serverPort, int queueLength) {
        this.serverPort = serverPort;
        this.queueLength = queueLength;

    }

    private boolean running;
    private ServerSocketChannel serverSocketChannel;
    private ByteBuffer buffer = ByteBuffer.allocate(1024);

    @Override
    public void run() {
        try {
            Selector selector = Selector.open();

            serverSocketChannel = ServerSocketChannel.open();
            serverSocketChannel.configureBlocking(false);
            ServerSocket serverSocket = serverSocketChannel.socket();
            serverSocket.bind(new InetSocketAddress(serverPort));
            System.out
                    .println("DeviceServerV0.2 - Going to Listen for connections on port: "
                            + serverSocket.getLocalPort());
            running = true;

            SelectionKey serverAcceptKey = serverSocketChannel.register(
                    selector, SelectionKey.OP_ACCEPT);
            int count = 0;
            while (running) {
                int keyCount = selector.select();
                System.out.println("keyCount: " + keyCount);
                Set<SelectionKey> selectedKeys = selector.selectedKeys();
                Iterator keyIterator = selectedKeys.iterator();
                while (keyIterator.hasNext()) {
                    SelectionKey key = (SelectionKey) keyIterator.next();

                    if ((key.readyOps() & SelectionKey.OP_ACCEPT) == SelectionKey.OP_ACCEPT) {

                        ServerSocketChannel ssc = (ServerSocketChannel) key
                                .channel();
                        SocketChannel sc = ssc.accept();
                        sc.configureBlocking(false);

                        // Here we add the new connection to the selector
                        SelectionKey newKey = sc.register(selector,
                                SelectionKey.OP_READ);

                        keyIterator.remove();

                    } else if ((key.readyOps() & SelectionKey.OP_READ) == SelectionKey.OP_READ) {
                        SocketChannel sc = (SocketChannel) key.channel();

                        // Going with this from the tutorial for now.
                        // TODO: Implement proper boolean controls here
                        while (true) {
                            buffer.clear();

                            int read = sc.read(buffer);

                            if (read <= 0) {
                                // System.out.println("Bytes read: " + read);
                                System.out
                                        .println("No more bytes, breaking loop.");

                                break;
                            } else {

                                buffer.flip();
                                String result = new String(buffer.array());
                                System.out
                                        .println("Buffer Contents: " + result);

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

                    }

                }

            }

        } catch (Exception e) {
            e.printStackTrace();
        }

    }

}

我也知道调用 ByteBuffer.array() 不会影响 byteBuffer 中的当前位置,因此它仍然看起来好像尚未读取缓冲区。我应该以某种方式处理这个问题还是足以调用 ByteBuffer.clear() 作为循环的开始?

我打算继续尝试构建一个使用线程池来处理请求的 NIO 服务器,但我想确保到目前为止我对 NIO 的理解是正确的。

当频道的readyOps()包含SelectionKey.OP_READ时,这意味着可以读取一些数据,而不是可以读取整个频道直到结束.但这就是你在 while(true) 循环中所做的;您正在阅读,因此轮询一个频道直到它结束。

正确的处理方法是读取一次并且不要尝试再次读取,除非下一次select报告有更多的数据要读取。