java NIO之SelectedKey

    SelectedKey是channel与Selector绑定的标记,每将一个channel注册到一个selector就会产生一个SelectedKey,并将这个SelectedKey放入到Selected的key set中,注意,key set 只能通过这种方式添加,不可以直接添加,但是可以手动移除。

    SelectedKey存在着两个集合,分别是interest set和ready set,二者的元素类型都是整数值,每一位代表一个标记,代表该key对应的channel支持/准备好什么操作。interest set在SelectedKey被创建时(channel注册时)指定,记录了此key对应的channel支持什么操作(accept,write,read,connect等),并且可以在以后通过interestOpt(int)改变;ready set则记录了此SelectedKey已经准备好了什么样的操作,ready set的值由selector设置和更新,不可以由用户去改变。ready set指定的操作说明该种操作已经就绪好,可以实行非阻塞操作了,但是不保证之后该key状态没有被修改(比如channel被关闭),如果之后key状态被改变又没有通过select操作更新(参见java.nio.channels.Selector),则操作可能依然会阻塞线程。

    SelectedKey为每种操作标记都定义了一个静态常量值,但是一个key具体指示什么操作,是由对应的channel决定的,SelectedChannel的各个子类都有自己所支持的操作,如果注册时被制定了不支持的操作,将会发生运行时错误。

    SelectedKey还为应用程序提供了一个附件区,可用于存放上层应用协议相关的数据,比如协议状态等。这个附件区是一个单独的对象,用attach设置,attachment获取。

    SelectedKey本身不是线程安全的,但是,操作它的selector要求实现同步措施,对数据的读写加上不同程度的同步锁。也就是说,同步策略交给具体的selector实现去做。

典型的NIO socket服务端代码如下所示:

public class PlainNioServer {
    public void serve(int port) throws IOException {
        ServerSocketChannel serverChannel = ServerSocketChannel.open();
        serverChannel.configureBlocking(false);
        ServerSocket ss = serverChannel.socket();
        InetSocketAddress address = new InetSocketAddress(port);
        ss.bind(address); //1
        Selector selector = Selector.open(); //2 
        serverChannel.register(selector, SelectionKey.OP_ACCEPT); //3 
        final ByteBuffer msg = ByteBuffer.wrap("Hi!\r\n".getBytes());
        for (; ; ) {
            try {
                selector.select(); //4 
            } catch (IOException ex) {
                ex.printStackTrace();
                // handle exception 
                break;
            }
            Set<SelectionKey> readyKeys = selector.selectedKeys(); //5 
            Iterator<SelectionKey> iterator = readyKeys.iterator();
            while (iterator.hasNext()) {
                SelectionKey key = iterator.next();
                iterator.remove();
                try {
                    if (key.isAcceptable()) { //6 
                        ServerSocketChannel server = (ServerSocketChannel) key.channel();
                        SocketChannel client = server.accept();
                        client.configureBlocking(false);
                        client.register(selector, SelectionKey.OP_WRITE | SelectionKey.OP_READ, msg.duplicate()); //7
                        System.out.println("Accepted connection from " + client);
                    }
                    if (key.isWritable()) { //8 
                        SocketChannel client = (SocketChannel) key.channel();
                        ByteBuffer buffer = (ByteBuffer) key.attachment();
                        while (buffer.hasRemaining()) {
                            if (client.write(buffer) == 0) { //9 
                                break;
                            }
                        }
                        client.close(); //10 
                    }
                } catch (IOException ex) {
                    key.cancel();
                    try {
                        key.channel().close();
                    } catch (IOException cex) { // 在关闭时忽略 
                    }
                }
            }
        }
    }
}

 

posted on 2018-02-17 10:46  J.M.Liu  阅读(1760)  评论(0编辑  收藏  举报