Java NIO Selector详解
扫描二维码
随时随地手机看文章
Selector selector = Selector.open();
将 Channel 注册到选择器中
channel.configureBlocking(false);SelectionKey key = channel.register(selector, SelectionKey.OP_READ);
注意,如果一个 Channel 要注册到 Selector 中,那么这个 Channel 必须是非阻塞的,即channel.configureBlocking(false);
因为 Channel 必须要是非阻塞的,因此 FileChannel 是不能够使用选择器的,因为 FileChannel 都是阻塞的.
Connect,即连接事件(TCP 连接), 对应于SelectionKey.OP_CONNECT。
Accept,即确认事件,对应于SelectionKey.OP_ACCEPT。
Read,即读事件,对应于SelectionKey.OP_READ, 表示 buffer 可读。
Write,即写事件,对应于SelectionKey.OP_WRITE, 表示 buffer 可写。
一个 Channel发出一个事件也可以称为对于某个事件, Channel 准备好了。因此一个 Channel 成功连接到了另一个服务器也可以被称为 connect ready。
int interestSet = SelectionKey.OP_READ | SelectionKey.OP_WRITE;
channel.register(selector, SelectionKey.OP_READ);channel.register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE);
int interestSet = selectionKey.interestOps();boolean isInterestedInAccept = interestSet & SelectionKey.OP_ACCEPT;boolean isInterestedInConnect = interestSet & SelectionKey.OP_CONNECT;boolean isInterestedInRead = interestSet & SelectionKey.OP_READ;boolean isInterestedInWrite = interestSet & SelectionKey.OP_WRITE;
int readySet = selectionKey.readyOps();selectionKey.isAcceptable();selectionKey.isConnectable();selectionKey.isReadable();selectionKey.isWritable();
Channel 和 Selector
Channel channel = selectionKey.channel();Selector selector = selectionKey.selector();
Attaching Object
selectionKey.attach(theObject);Object attachedObj = selectionKey.attachment();
SelectionKey key = channel.register(selector, SelectionKey.OP_READ, theObject);
通过 Selector 选择 Channel
注意:select()方法返回的值表示有多少个 Channel 可操作.
获取可操作的 Channel
SetselectedKeys = selector.selectedKeys(); IteratorkeyIterator = selectedKeys.iterator(); 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();}
-
通过 Selector.open() 打开一个 Selector. -
将 Channel 注册到 Selector 中, 并设置需要监听的事件(interest set) -
不断重复: *从 selected key 中获取 对应的 Channel 和附加信息(如果有的话)
关闭 Selector
完整的 Selector 例子
public class NioEchoServer {private static final int BUF_SIZE = 256;private static final int TIMEOUT = 3000;public static void main(String args[]) throws Exception {// 打开服务端 SocketServerSocketChannel serverSocketChannel = ServerSocketChannel.open();// 打开 SelectorSelector selector = Selector.open();// 服务端 Socket 监听8080端口, 并配置为非阻塞模式serverSocketChannel.socket().bind(new InetSocketAddress(8080));serverSocketChannel.configureBlocking(false);// 将 channel 注册到 selector 中.// 通常我们都是先注册一个 OP_ACCEPT 事件, 然后在 OP_ACCEPT 到来时, 再将这个 Channel 的 OP_READ// 注册到 Selector 中.serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);while (true) {// 通过调用 select 方法, 阻塞地等待 channel I/O 可操作if (selector.select(TIMEOUT) == 0) {System.out.print(".");continue;}// 获取 I/O 操作就绪的 SelectionKey, 通过 SelectionKey 可以知道哪些 Channel 的哪类 I/O 操作已经就绪.IteratorkeyIterator = selector.selectedKeys().iterator(); while (keyIterator.hasNext()) {SelectionKey key = keyIterator.next();// 当获取一个 SelectionKey 后, 就要将它删除, 表示我们已经对这个 IO 事件进行了处理.keyIterator.remove();if (key.isAcceptable()) {// 当 OP_ACCEPT 事件到来时, 我们就有从 ServerSocketChannel 中获取一个 SocketChannel,// 代表客户端的连接// 注意, 在 OP_ACCEPT 事件中, 从 key.channel() 返回的 Channel 是 ServerSocketChannel.// 而在 OP_WRITE 和 OP_READ 中, 从 key.channel() 返回的是 SocketChannel.SocketChannel clientChannel = ((ServerSocketChannel) key.channel()).accept();clientChannel.configureBlocking(false);//在 OP_ACCEPT 到来时, 再将这个 Channel 的 OP_READ 注册到 Selector 中.// 注意, 这里我们如果没有设置 OP_READ 的话, 即 interest set 仍然是 OP_CONNECT 的话, 那么 select 方法会一直直接返回.clientChannel.register(key.selector(), OP_READ, ByteBuffer.allocate(BUF_SIZE));}if (key.isReadable()) {SocketChannel clientChannel = (SocketChannel) key.channel();ByteBuffer buf = (ByteBuffer) key.attachment();long bytesRead = clientChannel.read(buf);if (bytesRead == -1) {clientChannel.close();} else if (bytesRead > 0) {key.interestOps(OP_READ | SelectionKey.OP_WRITE);System.out.println("Get data length: " + bytesRead);}}if (key.isValid() && key.isWritable()) {ByteBuffer buf = (ByteBuffer) key.attachment();buf.flip();SocketChannel clientChannel = (SocketChannel) key.channel();clientChannel.write(buf);if (!buf.hasRemaining()) {key.interestOps(OP_READ);}buf.compact();}}}}}
转载源:SegmentFault
特别推荐一个分享架构+算法的优质内容,还没关注的小伙伴,可以长按关注一下:
长按订阅更多精彩▼
如有收获,点个在看,诚挚感谢
免责声明:本文内容由21ic获得授权后发布,版权归原作者所有,本平台仅提供信息存储服务。文章仅代表作者个人观点,不代表本平台立场,如有问题,请联系我们,谢谢!





