如何使用多个频道的选择器?(nio)

时间:2014-07-29 18:34:39

标签: java io selector nio nonblocking

我正在阅读以下教程: http://tutorials.jenkov.com/java-nio/selectors.html

我觉得我不理解以下文章的很多细节,但我试图在文章末尾调用示例。

我的最终代码看起来如此:

 public static void main(String[] args) throws IOException {
        ServerSocketChannel channel = ServerSocketChannel.open();
        Selector selector = Selector.open();
        channel.configureBlocking(false);
        SelectionKey selectionKey = channel.register(selector, SelectionKey.OP_READ);
        while (true) {
            int readyChannels = selector.select();
            if (readyChannels == 0) continue;
            Set<SelectionKey> selectedKeys = selector.selectedKeys();
            Iterator<SelectionKey> keyIterator = selectedKeys.iterator();//cause of exception
            while (keyIterator.hasNext()) {
                SelectionKey key = keyIterator.next();
                if (key.isAcceptable()) {
                    // a connection was accepted by a ServerSocketChannel.
                } else if (key.isConnectable()) {
                    // a connection was established with a remote server.
                } else if (key.isReadable()) {
                    // a channel is ready for reading
                } else if (key.isWritable()) {
                    // a channel is ready for writing
                }
                keyIterator.remove();
            }
        }
    }

作为这种方法调用的结果,我看到了以下堆栈跟踪:

Exception in thread "main" java.lang.IllegalArgumentException
    at java.nio.channels.spi.AbstractSelectableChannel.register(AbstractSelectableChannel.java:196)
    at java.nio.channels.SelectableChannel.register(SelectableChannel.java:277)
    ...

更新

我的完整代码变体:

public class NioSelectorTest {
    public static void main(String[] args) {
        new Thread(new Receiver()).start();
        new Thread(new Sender()).start();
    }

}

class Receiver implements Runnable {
    private static byte[] data = new byte[255];

    public void run() {
        try {
            for (int i = 0; i < data.length; i++)
                data[i] = (byte) i;

            ServerSocketChannel server = ServerSocketChannel.open();
            server.configureBlocking(false);

            server.socket().bind(new InetSocketAddress(9000));
            Selector selector = Selector.open();
            server.register(selector, SelectionKey.OP_ACCEPT);

            while (true) {
                selector.select();
                Set readyKeys = selector.selectedKeys();
                Iterator iterator = readyKeys.iterator();
                while (iterator.hasNext()) {
                    SelectionKey key = (SelectionKey) iterator.next();
                    iterator.remove();
                    if (key.isAcceptable()) {
                        SocketChannel client = server.accept();
                        System.out.println("Accepted connection from " + client);
                        client.configureBlocking(false);
                        ByteBuffer source = ByteBuffer.wrap(data);
                        SelectionKey key2 = client.register(selector, SelectionKey.OP_WRITE);
                        key2.attach(source);
                    } else if (key.isWritable()) {
                        SocketChannel client = (SocketChannel) key.channel();
                        ByteBuffer output = (ByteBuffer) key.attachment();
                        if (!output.hasRemaining()) {
                            output.rewind();
                        }
                        client.write(output);
                    }

                    key.channel().close();

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

class Sender implements Runnable {

    public void run() {
        try {
            SocketChannel sChannel = SocketChannel.open();
            sChannel.configureBlocking(false);
            sChannel.connect(new InetSocketAddress("localhost", 9000));
            while (!sChannel.finishConnect()) {
                ByteBuffer buf = ByteBuffer.allocateDirect(1024);
                buf.put((byte) 0xFF);

                buf.flip();
                int numBytesWritten = sChannel.write(buf);
                System.out.println("sent " + numBytesWritten + " bytes");
            }
        } catch (IOException exception) {
            exception.printStackTrace();
        }
    }
}

在控制台中我看到了

  

来自java.nio.channels.SocketChannel的已接受连接[已连接   local = / 127.0.0.1:9000 remote = / 127.0.0.1:50940]

在调试中我看到我无法进入这个循环:

while (!sChannel.finishConnect()) {
...

2 个答案:

答案 0 :(得分:0)

        SelectionKey selectionKey = channel.register(selector, SelectionKey.OP_READ);

对于ServerSocketChannel,这应该是OP_ACCEPT。

答案 1 :(得分:-1)

在Java文档中,编写了register方法抛出

  

IllegalArgumentException - 如果ops中的某个位与此通道支持的操作不对应,即,如果设置&amp; ~validOps()!= 0

(资料来源:http://bit.ly/1lSBdX8

我认为你需要首先绑定一个特定的端口,并在该SocketChannel上注册选择器。因此,代码的开头应如下所示:

ServerSocketChannel channel = ServerSocketChannel.open();
channel.socket().bind(new InetSocketAddress(8080)); // 8080 is the port you'll be listening
SocketChannel socketChannel = serverSocketChannel.accept();
Selector selector = Selector.open();
socketChannel.configureBlocking(false);
SelectionKey selectionKey = socketChannel.register(selector, SelectionKey.OP_READ);
...