四、Selector选择器

18 篇文章 1 订阅

接着上一章的那个比喻,如果我们把Buffer缓冲区看做码头,Channel通道看做航道的话,那么Selector选择器可以看做海上的一个调度中心,一个调度中心可以监听多个航道的事件,当然,这需要航道归属在这个调度中心的编制下。

创建一个Selector

Selector selector = Selector.open();

将Channel注册在某个Selector的管辖范围之下

Selector是一个对象,它可以被注册到很多个Channel上,监听各个Channel上发生的事件,并且能够根据事件情况决定Channel读写。这样,通过一个线程管理多个Channel,就可以处理大量网络连接了。

channel.register(selector, SelectionKey.OP_ACCEPT);

上面这行代码是有返回值的,这个方法会返回一个SelectionKey类型的结构。这个SelectionKey是NIO中事务处理的一个关键中间点,通过SelectionKey我们可以获取很多信息,可以说SelectionKey是整个NIO的业务大脑,关于SelectionKey,我将会放在下一章进行说明,毕竟自己目前的理解还不够深。

Selector如何选择就绪的通道

//这个方法可能会阻塞,直到至少有一个已注册的事件发生,或者当一个或者更多的事件发生时
selector.select();

NIO明明是非阻塞的IO,为何会阻塞

这是第一章Java NIO学习(一)NIO相关概念中说的内容,作为非阻塞的IO操作,NIO为何会有这么一个可能会造成阻塞的方法呢,别急,selector对象有很多方法可以解决阻塞的问题!

//阻塞在select()方法上的线程也可以立刻返回,不阻塞
selector.selectNow();

//可以设置超时时间,防止进程阻塞
selector.select(long timeout);

//可以唤醒阻塞状态下的selector
selector.wakeup();

选择器-多路复用器使用示例

具体详细使用示例请看:多路复用器-selector单线程版本

/**
 * 初始化服务器端信息
 */
private void initServerSocket(){
    try {
        serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.bind(new InetSocketAddress(serverPort));//绑定端口号
        System.out.println("step1 : new ServerSocket(" + serverPort+ ") ");
        serverSocketChannel.configureBlocking(false);//设置服务端非阻塞模式
        /**
         * //打开多路复用器
         */
        selector = Selector.open();
        /**
         * 把服务端注册到多路复用器当中,并监听一个网络行为---》OP_ACCEPT
         *
         * Interest Set
         * 监听的Channel通道触发了一个事件意思是该事件已经就绪。
         * 一个channel成功连接到另一个服务器称为”连接就绪“。
         * 一个server socket channel准备号接收新进入的连接称为”接收就绪“。
         * 一个有数据可读的通道可以说是”读就绪“。
         * 一个等待写数据的通道可以说是”写就绪“。
         * 这四种事件用SelectionKey的四个常量来表示:
         *     SelectionKey.OP_CONNECT
         *     SelectionKey.OP_ACCEPT
         *     SelectionKey.OP_READ
         *     SelectionKey.OP_WRITE
         */
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
    } catch (IOException e) {
        e.printStackTrace();
    }
}

/**
 * 客户端连接建立处理类
 * @param selectionKey
 */
private void acceptHandler(SelectionKey selectionKey){
    try {
        /**
         * 由于在之前已经把ServerSocketChannel
         * <p>serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);</p>
         * 已经注册到多路复用器(selector)中,因此现阶段可以直接把服务端通道直接取出
         */
        ServerSocketChannel serverSocketChannel = (ServerSocketChannel)selectionKey.channel();
        /**
         * 调用accept接口从服务端通道中获取客户端连接
         */
        SocketChannel clientSocketChannel = serverSocketChannel.accept();
        /**
         * 设置客户端连接也为非阻塞状态
         */
        clientSocketChannel.configureBlocking(false);

        /**
         * 创建一个8字节的数据缓冲区
         * 暂且不论该缓冲区大小是否足够
         * 该缓冲区的作用为:******一个客户端通道对应一个数据缓冲区,防止缓冲区公用
         */
        ByteBuffer byteBuffer = ByteBuffer.allocateDirect(8192);
        /**
         * 把新的客户端连接也注册到多路复用器当中,并且注册监听事件为读取就绪
         * 这样在新的<p>SelectionKey</p>  中就即可以拿到自己的客户端,也可以拿到与客户端绑定的缓冲区
         *
         * 附加选项---byteBuffer
         */
        SelectionKey clientSelectionKey = clientSocketChannel.register(selector,SelectionKey.OP_READ,byteBuffer);
        System.out.println("--------------------------------------------------");
        System.out.println("------新客户端进入------:" + clientSocketChannel.getRemoteAddress());
        System.out.println("--------------------------------------------------");
    } catch (IOException e) {
        e.printStackTrace();
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值