为什么客户端无法在这个 java nio 程序中连接到服务器?

Why client can't connect to server in this java nio program?

我正在阅读 Doug Lea 的 Scalable I/O in Java,我遵循了 基本反应器设计 示例代码。但是我启动服务器后,客户端无法连接到服务器。

这是 Reactor class:

class Reactor implements Runnable {

    private static final Logger logger = LogManager.getLogger();

    final Selector selector;
    final ServerSocketChannel serverSocket;

    public Reactor(int port) throws IOException {
        selector = Selector.open();
        serverSocket = ServerSocketChannel.open();
        serverSocket.bind(new InetSocketAddress(port));
        serverSocket.configureBlocking(false);
        SelectionKey sk = serverSocket.register(selector, SelectionKey.OP_ACCEPT);
        sk.attach(new Acceptor());
        logger.info("server started.");
    }

    @Override
    public void run() {
        while (!Thread.interrupted()) {
            for (final Iterator<SelectionKey> it = selector.selectedKeys().iterator(); it.hasNext(); it.remove()) {
                dispatch(it.next());
            }
        }
    }

    private void dispatch(SelectionKey key) {
        Runnable r = (Runnable) key.attachment();
        if (r != null) {
            r.run();
        }
    }

    private final class Acceptor implements Runnable {
        @Override
        public void run() {
            try {
                SocketChannel c = serverSocket.accept();
                if (c != null) {
                    new Handler(selector, c);
                }
            } catch (IOException ex) {
                ex.getMessage();
            }
        }
    }

    public static void main(String[] args) throws IOException {
        new Reactor(9000).run();
    }
}

处理程序class

final class Handler implements Runnable {
    private static final Logger logger = LogManager.getLogger();

    final SocketChannel c;
    final SelectionKey key;
    ByteBuffer buffer = ByteBuffer.allocate(1024);

    public Handler(Selector sel, SocketChannel c) throws IOException {
        this.c = c;
        c.configureBlocking(false);
        key = c.register(sel, SelectionKey.OP_READ | SelectionKey.OP_WRITE);
        logger.info("client connected: " + c);
    }

    void read() throws IOException {
        if (!buffer.hasRemaining()) {
            return;
        }
        c.read(buffer);
    }

    void process() {/* */}

    void write() throws IOException {
        buffer.flip();
        c.write(buffer);
        c.close();
    }

    @Override
    public void run() {
        try {
            read();
            process();
            write();
        } catch (IOException ex) {
            ex.getMessage();
        }
    }
}

我在 idea 中启动服务器,然后 server started 在控制台打印
但是当我在终端输入 telnet localhost 9000 后, client connected: 没有出现。

我不得不稍微更改 Reactor 运行 方法。您必须致电 selector.select()selector.selectNow():

    @Override
    public void run() {
        while (!Thread.interrupted()) {
            try {
                int ready = selector.selectNow();
                if (ready == 0){
                    continue;
                }

                Set<SelectionKey> selected = selector.selectedKeys();
                Iterator<SelectionKey> it = selected.iterator();
                while (it.hasNext()) {
                    SelectionKey key = it.next();
                    if(key.isAcceptable() || key.isReadable()) {
                        dispatch(key);
                    }                   
                }
                selected.clear();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

允许客户端连接。

为了启用来自 Handler 的回显服务,我实现了这个:

final class Handler implements Runnable {
    private static final Logger logger = LogManager.getLogger();
    final SocketChannel c;
    final SelectionKey key;
    ByteBuffer buffer = ByteBuffer.allocate(1024);

    public Handler(Selector selector, SocketChannel c) throws IOException {
        this.c = c;
        c.configureBlocking(false);
        logger.info("client connected: " + c);

        key = c.register(selector, 0);
        key.attach(this);
        key.interestOps(SelectionKey.OP_READ);
        selector.wakeup();
    }


    @Override
    public void run() {
        try {
            SocketChannel client = (SocketChannel) key.channel();
            client.read(buffer);
            if (new String(buffer.array()).trim().equals("close")) {
                client.close();
                System.out.println("close connection");
            }

            buffer.flip();
            client.write(buffer);
            buffer.clear();
        } catch (IOException ex) {
            ex.getMessage();
        }
    }
}

注册 Handler 实例进行读取,然后根据可读选择键调用此实例的 运行 方法来处理读取。