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

huangapple go评论73阅读模式
英文:

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

问题

我正在阅读道格·里的《Java中的可扩展I/O》,并且我按照基本反应器设计示例代码进行操作。但是在我启动服务器之后,客户端无法连接到服务器。

以下是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("服务器已启动。");
    }

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

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 sel, SocketChannel c) throws IOException {
        this.c = c;
        c.configureBlocking(false);
        key = c.register(sel, SelectionKey.OP_READ | SelectionKey.OP_WRITE);
        logger.info("客户端已连接:" + 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中启动服务器,然后在控制台中打印出服务器已启动。但是,在终端中输入telnet localhost 9000之后,客户端已连接并未出现。

英文:

I'm reading Doug Lea's Scalable I/O in Java, and I followed the Basic Reactor Design example code. But after I started server, the client can't connect to server.<br>

Here is the 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(&quot;server started.&quot;);
    }

    @Override
    public void run() {
        while (!Thread.interrupted()) {
            for (final Iterator&lt;SelectionKey&gt; 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();
    }
}

Handler 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(&quot;client connected: &quot; + 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();
        }
    }
}

I start server in idea and then server started is printed in console<br>
But after I enter telnet localhost 9000 in terminal, client connected: doesn't appear.

答案1

得分: 1

我不得不稍微修改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实例,然后在可读的选择键上调用此实例的运行方法来处理读取。

英文:

I had to change the Reactor run method a bit. you have to call selector.select() or selector.selectNow():

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

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

that allowed the client to connect.

in order to enable an echo service from Handler I implemented this:

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(&quot;client connected: &quot; + 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(&quot;close&quot;)) {
                client.close();
                System.out.println(&quot;close connection&quot;);
            }

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

register the Handler instance for reading and then upon a readable selection key the run method of this instance is called to handle the reading.

huangapple
  • 本文由 发表于 2020年3月16日 23:33:46
  • 转载请务必保留本文链接:https://go.coder-hub.com/60708854.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定