Selector 选择器

简介: Selector 选择器

前言

工欲善其事必先利其器, 讲了好几篇文章的Netty相关, 今天讲一下原始的 Java NIO 的Selector选择器, 本篇将从基本介绍到API相关全部介绍一遍


基本介绍

JavaNIO使用非阻塞的 IO 方式, 可以使用一个线程去处理多个客户端连接, 这个时候就会使用到Selector选择器

Selector可以检测到多个注册的通道上是否有事件发生(多个Channel以事件的方式可以注册到一个Selector上). 如果有事件发生, 就去获取事件然后针对每一个事件进行相应的处理, 这样就实现了只用一个线程去管理多个通道

通过Selector使得只有在连接真正有读写事件发生的时候, 才会进行读写, 大大减少了系统开销

Netty中的 IO线程NioEventLoop聚合了Selector, 可以同时并发的处理成百上千个客户端的连接

API

可以看到Selector是一个抽象类, 如图所示, 是Selector的所有方法, 接下来我们就讲解一下Selector的常用方法

网络异常,图片无法展示
|

  • open(): 得到一个选择器对象
  • select():无超时时间的select过程, 一直等待, 直到发现有CHannel可以进行 IO操作
  • select(long timeout):监控所有注册的Channel, 当其中的CHannel有 IO操作 可以进行时, 将这些Channel对应的SelectionKey找到, 参数用户设置超时时间
  • wakup():唤醒selector
  • selectNow():不阻塞, 立即返回
  • selectKeys():返回所有发生事件的Channel对应的SelectionKey的集合, 通过SelectionKey可以找到对应的Channel
  • keys():返回所有CHannel对应的SelectionKey的集合, 通过SelectionKey能找到对应的CHannel

NIO 中存在ServerSocketChannel功能类似于ServerSocket, SocketCahnnel类似于Select

客户端发起连接时服务端工作流程

示例代码

public static void main(String[] args) throws IOException {
    // 异常向上抛出
    ServerSocketChannel server= ServerSocketChannel.open();
    // 获取一个选择器对象
    Selector selector = Selector.open();
    // 注册 serverSocketChannel 到 selector, 关注 OP_ACCEPT 事件
    server.register(selector, SelectionKey.OP_ACCEPT);
    // 绑定端口
    server.socket().bind(new InetSocketAddress(8888));
    // 设置 serverSocketChannel 为非阻塞模式
    server.configureBlocking(false);
    for (;;){
        // 没有事件发生
        if (selector.select(1000) == 0){
            continue;
        }
        // 有事件发生, 找到发生事件的 Channel 对应的 SelectionKey 的集合
        Set<SelectionKey> selectionKeys = selector.selectedKeys();
        // 遍历
        Iterator<SelectionKey> iterator = selectionKeys.iterator();
        while(iterator.hasNext()){
            final SelectionKey next = iterator.next();
            // 发生 OP_ACCEPT 事件, 处理连接请求
            if (next.isAcceptable()){
                final SocketChannel accept = server.accept();
                // 将 socketChannel 注册到 selector, 关注 OP_READ 事件, 并给 socketChannel 关联 Buffer
                accept.register(selector, SelectionKey.OP_READ, ByteBuffer.allocate(2048));
            }
            // 如果是发生读事件, 客户端读取数据
            else if (next.isReadable()){
                final SocketChannel channel = (SocketChannel) next.channel();
                final ByteBuffer buffer =(ByteBuffer) next.attachment();
                channel.read(buffer);
            }
            // 手动从集合中移除当前的 selectionKey, 防止重复处理事件
            iterator.remove();
        }
    }
}
复制代码

在上述服务端示例代码中, 服务端的工作流程为:

  • 当客户端发起连接时, 会通过ServerSocketChannel创建对应的SocketChannel
  • 调用SelectChannel的注册方法将其注册到Selector上, 注册方法会返回一个SelectionKey, 将该SelectionKey放入到SelectionKeys集合中, 此时, SelectKeySelector关联, 也和SocketChannel关联
  • Selector调用select(), select(timeout), selectNow()方法对内部的SelectionKeys中的SelectionKey所对应的SocketChannel进行监听
  • 通过SelectionKey找到有事件发生的SocketChannel, 完成数据处理





目录
相关文章
|
2月前
选择器
选择器。
32 2
|
2月前
选择器(2)
选择器(2)。
44 2
|
2月前
before选择器
before选择器。
47 1
|
2月前
after选择器
after选择器。
30 1
before和after选择器
before和after选择器
60 0
|
Linux
4.2 Selector
4.2 Selector
59 0
html+css实战67-02选择器-子代选择器
html+css实战67-02选择器-子代选择器
144 0
html+css实战67-02选择器-子代选择器
|
Web App开发 前端开发 JavaScript
神奇的选择器 :focus-within
神奇的选择器 :focus-within
174 0
神奇的选择器 :focus-within
|
Web App开发 前端开发 JavaScript
神奇的选择器 :focus-within
神奇的选择器 :focus-within
145 0