码恋 码恋

ALL YOUR SMILES, ALL MY LIFE.

目录
Netty 系列笔记之 Java NIO 核心组件 Selector
/    

Netty 系列笔记之 Java NIO 核心组件 Selector

一、概述

Selector 中文为 选择器。在 Java 的 NIO 中,多个 Channel 可注册到同一个 Selector 中。Selector 通过轮询的方式查看 Channel 是否处于可读可写的状态。这样一个线程可以管理多个 Channel ,也就能管理多个网络连接,实现多路复用,故 Selector 也称为 多路复用器

二、Selector 的使用

下面是一个简单的使用示例:

try (ServerSocketChannel channel = ServerSocketChannel.open(); 
  Selector selector = Selector.open();) {
  channel.bind(new InetSocketAddress(9999));
  channel.configureBlocking(false);
  channel.register(selector, SelectionKey.OP_ACCEPT);
  while (true) {
    if (selector.select() == 0) {
      continue;
    }
    Set<SelectionKey> selectionKeys = selector.selectedKeys();
    Iterator<SelectionKey> iterator = selectionKeys.iterator();
    while (iterator.hasNext()) {
      SelectionKey key = iterator.next();
      if (key.isAcceptable()) {
        // do sth...
       }
       iterator.remove();
    }
  }
} catch (Exception e) {
   e.printStackTrace();
}
1、创建 Selector

通过 Selector.open() 方法创建 Selector :

Selector selector = Selector.open();
2、向 Selector 注册 Channel

为了使 Selector 管理 Channel ,需要将 Channel 注册到 Selector 中,通过调用 SelectableChannel.register() 方法。

channel.configureBlocking(false);
channel.register(selector, SelectionKey.OP_ACCEPT);
  • 值得注意的是,与 Selector 配合使用时,Channel 必须处于非阻塞模式。这意味着 FileChannel 不能与 Selector 一起使用,而 FileChannel 也没有继承 SelectableChannel 或者它的子类。
  • register() 方法的第二个参数是 Selector 监听 Channel 时感兴趣的事件集合。可以监听下面四种事件:
事件常量描述
ReadSelectionKey.OP_READ读就绪事件,此时 Buffer 可读
WriteSelectionKey.OP_WRITE写就绪事件,此时 Buffer 可写
ConnectSelectionKey.OP_CONNECT连接完成事件
AcceptSelectionKey.OP_ACCEPT接收新的连接事件

一个 Channel 对多个事件感兴趣时,可以用或运算符组合:

channel.register(selector, SelectionKey.OP_ACCEPT | SelectionKey.OP_CONNECT);
3、SelectionKey

向 Selector 注册一个 Channel 后,会返回一个 SelectionKey 对象,其保存了 Channel 与 Selector 的对应关系,具体如下:

  • channel :保存当前 Channel
  • selector :保存当前 Selector
  • interestOps :感兴趣的事件集合
  • readyOps : 就绪的事件集合
  • attachment :附加的对象

同时提供了相应的方法获取上述属性,可调用 selectionKey.attach(theObject); 方法或在向 Selector 注册 Channel 时传入 attachment 附加对象,可用于方便识别特定 Channel 。

4、通过 Selector 选择就绪的 Channel

在向 Sector 注册 Channel 后,就可以调用 select() 方法返回感兴趣的事件已经就绪的通道数量。它有三个重载方法:

// 阻塞到至少有一个感兴趣的事件就绪,然后返回就绪 Channel 数量
public abstract int select() throws IOException;

// 阻塞直到超时
public abstract int select(long timeout) throws IOException;

// 不阻塞,立即返回
public abstract int selectNow() throws IOException;

select() 方法返回大于 0 时,即表示有通道已经就绪了,这时就可以调用 Selector.selectKeys() 方法获得 “已选择键集(selected key set)” 中就绪的 Channel 。

❤注意:

1、当有就绪的通道时,一定要先调用 select() 方法,才能够将其加入已选择键集,直接调用 selectKeys() 方法是拿不到通道对应的 SelectionKey 对象的
2、每次迭代末尾需要调用 keyIterator.remove() 方法,Selector 不会自己从已选择键集中移除 SelectionKey 对象。

三、结语

关于 Selector 的源码实现可参考 :《Selector 实现原理》



❤ 转载请注明本文地址或来源,谢谢合作 ❤


center