Почему клиент не может подключиться к серверу в этой программе java nio? - PullRequest
0 голосов
/ 16 марта 2020

Я читаю Масштабируемый ввод / вывод Дуга Ли в Java, и я следовал примеру кода Basi c Reactor Design . Но после того, как я запустил сервер, клиент не может подключиться к серверу.

Вот класс Reactor:

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

Класс обработчика

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

Я запускаю сервер по идее, а затем сервер запущен печатается в консоли
Но после ввода telnet localhost 9000 в терминале, клиент подключен: не появляется.

1 Ответ

1 голос
/ 16 марта 2020

Мне пришлось немного изменить метод запуска 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 для чтения, а затем по читаемой клавише выбора вызывается метод run этого экземпляра для обработки чтения.

...