NIO-Selector详解

NIO-Selector详解

Selector概述

Selector选择器,也可以称为多路复⽤器。它是Java NIO的核⼼组件之⼀,⽤于检查⼀个或多个Channel的状态是否处于可读、可写、可连接、可接收等。通过⼀个Selector选择器管理多个Channel,可以实现⼀个线程管理多个Channel对应的⽹络连接。使⽤单线程管理多个Channel可以避免多线程的线程上下⽂切换带来的额外开销。

SelectableChannel可选择通道

只有SelectableChannel才能被Selector管理,⽐如所有的Socket通道。⽽FileChannel并没有继承SelectableChannel,因此不能被Selector管理。

Channel注册到Selector上

Channel通过注册的⽅式关联Selector。⼀个Channel可以注册到多个Selector上,但在某⼀个Selector上只能注册⼀次。注册时需要告知Selector,Selector需要对通道的哪个操作感兴趣。

public final SelectionKey register(Selector sel, int ops) throws ClosedChannelException{
    return register(sel, ops, null);
}

通道的操作如下:

  • 可读:SelectionKey.OP_READ

  • 可写:SelectionKey.OP_WRITE

  • 可连接:SelectionKey.OP_CONNECT

  • 可接收:SelectionKey.OP_ACCEPT

⽐如channel调⽤register⽅法进⾏注册到Selector,并告知Selector对哪个操作感兴趣:

channel.register(selector, SelectionKey.OP_READ);

也可以同时注册多个操作:

channel.register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE);

选择器会查询每个⼀个channel的操作事件,如果是该channel注册的操作已就绪,则进⾏响应。注意,这⾥channel的操作指的是channel完成某个操作的条件,表示该channel对于该操作已处于就绪状态。⽐如ServerSocketChannel已准备好接收新的连接,那么它注册的 SelectionKey.OP_ACCEPT 操作就处于就绪状态。⼜⽐如SocketChannel已准备好去连接Server服务器,那么它注册的SelectionKey.OP_CONNECT 操作就处于就绪状态。于是Selector就可以触发之后的动作。

SelectionKey选择键

SelectionKey封装了Channel和注册的操作。

当Selector调⽤select()⽅法时,会轮询所有注册在它身上的Channel,查看是否有处于某个操作(已注册到selector上的)就绪状态的Channel,把这些Channel放⼊到SelectionKey的集合中。

Selector的使用

  • 创建Selector 通过Selector的open⽅法创建Selector对象。

    // 创建Selector
    Selector selector = Selector.open();
  • Channel注册到Selector上 Channel必须处于非阻塞模式才能注册到Selector上

    package com.my.io.selector;
    ​
    import java.io.IOException;
    import java.net.InetSocketAddress;
    import java.nio.channels.SelectionKey;
    import java.nio.channels.Selector;
    import java.nio.channels.ServerSocketChannel;
    ​
    /**
     * @author zhupanlin
     * @version 1.0
     * @description: selector的使用:注册channel到selector上
     * @date 2024/1/26 11:02
     */
    public class Demo1 {
    ​
        public static void main(String[] args) throws IOException {
            // 1.创建Selector
            Selector selector = Selector.open();
            // 2.获得Channel
            ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
            // 3.设置成非阻塞的模式
            serverSocketChannel.configureBlocking(false);
            // 4.绑定端口
            serverSocketChannel.bind(new InetSocketAddress(9001));
            // 5.注册channel到selector上
            SelectionKey selectionKey = serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
            
        }
        
    }
  • Selector轮询就绪状态的Channel

    Selector通过调⽤select⽅法轮询已就绪的通道操作。select⽅法是阻塞的,直到⾄少有⼀个通道的注册操作已就绪。当完成select⽅法调⽤后,被选中的已就绪的所有channel通过Selector的selectedKeys()⽅法获得,该⽅法获得的是⼀个SelectionKey集合,其中每⼀个SelectionKey都表示⼀个Channel。于是可以根据SelectionKey的注册操作来做具体的业务处理。

    package com.my.io.selector;
    ​
    import java.io.IOException;
    import java.net.InetSocketAddress;
    import java.nio.channels.SelectionKey;
    import java.nio.channels.Selector;
    import java.nio.channels.ServerSocketChannel;
    import java.util.Iterator;
    import java.util.Set;
    ​
    /**
     * @author zhupanlin
     * @version 1.0
     * @description: Selector轮询就绪状态的Channel
     * @date 2024/1/26 11:10
     */
    public class Demo2 {
    ​
        public static void main(String[] args) throws IOException {
            Selector selector = Selector.open();
            // serverSocketChannel
            ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
            serverSocketChannel.configureBlocking(false);
            // 绑定端口
            serverSocketChannel.bind(new InetSocketAddress(9001));
            // 注册
            SelectionKey selectionKey = serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
            // selector轮询
            while (true){
                // 阻塞等待某个操作就绪状态的channel
                selector.select();
                // 获得一个集合,里面包含了这次selector执行select方法获得的发生就绪状态的多个channel
                Set<SelectionKey> selectionKeys = selector.selectedKeys();
                // 遍历
                Iterator<SelectionKey> iterator = selectionKeys.iterator();
                while (iterator.hasNext()){
                    SelectionKey key = iterator.next();
                    if (key.isReadable()){
                        // 处理读状态的业务
                        
                    }else if (key.isAcceptable()){
                        // 处理接收状态的业务
                        
                    }else if (key.isConnectable()){
                        // 处理连接状态的业务
                        
                    }else if (key.isWritable()){
                        // 处理写状态的业务
                        
                    }
                    // 保证下次channel有就绪状态的操作发生时可以被selector轮询到
                    iterator.remove();
                }
            }
        }
        
    }
    ​

Selector示例

  • 实现NIO通信的服务端

    package com.my.io.selector;
    ​
    ​
    import java.io.IOException;
    import java.net.InetSocketAddress;
    import java.nio.ByteBuffer;
    import java.nio.channels.*;
    import java.util.Iterator;
    import java.util.Set;
    ​
    /**
     * @author zhupanlin
     * @version 1.0
     * @description: 服务端demo
     * @date 2024/1/26 11:45
     */
    public class ServerDemo {
    ​
        public static void main(String[] args) throws IOException {
            // 获得Channel
            ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
            // 设置成非阻塞
            serverSocketChannel.configureBlocking(false);
            // 绑定端口号
            serverSocketChannel.bind(new InetSocketAddress(9001));
            // 获得Selector
            Selector selector = Selector.open();
            // 把channel注册到selector上面
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
            // 让selector轮询监听
            while (true){
                // 阻塞直到有通道就绪
                selector.select();
                Set<SelectionKey> selectionKeys = selector.selectedKeys();
                // 获取有动作的selectionKey == channel
                Iterator<SelectionKey> iterator = selectionKeys.iterator();
                while (iterator.hasNext()){
                    SelectionKey selectionKey = iterator.next();
                    handle(selectionKey);
                    // 删除key,表示处理完成
                    iterator.remove();
                }
            }
        }
    ​
        private static void handle(SelectionKey selectionKey) throws IOException {
            if (selectionKey.isAcceptable()){
                // 当服务端处于接收的就绪状态
                // 获得selectionKey中的channel
                ServerSocketChannel serverSocketChannel = (ServerSocketChannel)selectionKey.channel();
                // 接收客户端连接,获得socketChannel
                SocketChannel socketChannel = serverSocketChannel.accept();
                // 设置成非阻塞状态,否则无法被selector复用
                socketChannel.configureBlocking(false);
                // 把socketChannel注册到selector上,让selector对socketChannel的read操作感兴趣
                socketChannel.register(selectionKey.selector(), SelectionKey.OP_READ);
            }else if (selectionKey.isReadable()){
                // 当socketChannel处于读数据的就绪状态
                SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
                // 读取socketChannel中的数据
                //设置成非阻塞
                socketChannel.configureBlocking(false);
                // 创建Buffer
                ByteBuffer buffer = ByteBuffer.allocate(1024);
                // 读数据
                int len = 0;
                while ((len = socketChannel.read(buffer)) > 0){
                    // 翻转
                    buffer.flip();
                    System.out.println(new String(buffer.array(), 0, len));
                    // 清除buffer中的数据
                    buffer.clear();
                }
                socketChannel.register(selectionKey.selector(), SelectionKey.OP_WRITE);
    ​
            }else if (selectionKey.isWritable()){
                
            }
        }
    ​
    }
  • 客户端

    package com.my.io.selector;
    ​
    import java.io.IOException;
    import java.net.InetSocketAddress;
    import java.nio.ByteBuffer;
    import java.nio.channels.SocketChannel;
    ​
    /**
     * @author zhupanlin
     * @version 1.0
     * @description: 客户端demo
     * @date 2024/1/26 11:28
     */
    public class ClientDemo {
    ​
        public static void main(String[] args) throws IOException {
            // 创建Channel
            SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("localhost", 9001));
            // 设置成非阻塞模式
            socketChannel.configureBlocking(false);
            // 得到buffer
            ByteBuffer buffer = ByteBuffer.allocate(1024);
            // 把数据写入到buffer中
            buffer.put("hello selector".getBytes());
            // 反转buffer
            buffer.flip();
            // 把buffer中的数据写入到channel中
            socketChannel.write(buffer);
            // 关闭
            socketChannel.close();
        }
        
    }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/351993.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

深入浅出AI落地应用分析:AI视频生成Top 5应用

接下俩会每周集中体验一些通用或者垂直的AI落地应用&#xff0c;主要以一些全球或者国外国内排行较前的产品为研究对象&#xff0c;「AI 产品榜&#xff1a; aicpb.com」以专题的方式在博客进行分享。 一、Loom 二、Runway 产品链接&#xff1a;https://app.runwayml.com/ …

企业职能部门员工忙闲不均,如何调动积极性?

案例企业背景&#xff1a; 某企业隶属于中国航天科技集团公司&#xff0c;致力于光纤陀螺系统、微机电惯性系统、光纤传感系统等高新技术产品的研发。公司具有雄厚的新型惯导和光电传感技术基础&#xff0c;多年来开创了我国光纤陀螺技术在武器、卫星和载人飞船等多个任务上的…

代码随想录Day32 | 122.买卖股票的最佳时机II 55. 跳跃游戏 45.跳跃游戏II

代码随想录Day32 | 122.买卖股票的最佳时机II 55. 跳跃游戏 45.跳跃游戏II 122.买卖股票的最佳时机II55.跳跃游戏45.跳跃游戏II 122.买卖股票的最佳时机II 文档讲解&#xff1a;代码随想录 视频讲解&#xff1a; 贪心算法也能解决股票问题&#xff01;LeetCode&#xff1a;122.…

【笔记】顺利通过EMC试验(16-41)-视频笔记

目录 视频链接 P1:电子设备中有哪些主要骚扰源 P2:怎样减小DC模块的骚扰 P3:PCB上的辐射源究竟在哪里 P4:怎样控制PCB板的电磁辐射 P5:多层线路板是解决电磁兼容问题的简单方法 P6:怎样处理地线上的裂缝 P7:怎样降低时钟信号的辐射 P8:为什么IO接口的处理特别重要 P9…

ARIMA模型:Python实现

ARIMA模型&#xff1a;Python实现 自回归移动平均模型&#xff08;ARIMA&#xff09;是一种经典的时间序列分析和预测方法。前期已介绍了ARIMA的概念和公式&#xff0c;本文将介绍ARIMA模型的理论基础&#xff0c;并提供详细的Python代码实现&#xff0c;帮助读者了解如何应用…

VS生成报错:MSB8036 The Windows SDK version 8.1 was not found.找不到 Windows SDK 版本 8.1

目录 一、查看本机SDK二、 解决法一&#xff1a;适配本电脑的SDK法二&#xff1a;下载SDK 8.1 VS生成报错&#xff1a;MSB8036 找不到 Windows SDK 版本 8.1。请安装所需版本的 Windows SDK&#xff0c;或者在项目属性页中或通过右键单击解决方案并选择“重定解决方案目标”来更…

用ChatGPT写申请文书写进常春藤联盟?

一年前&#xff0c;ChatGPT 的发布引发了教育工作者的恐慌。现在&#xff0c;各大学正值大学申请季&#xff0c;担心学生会利用人工智能工具伪造入学论文。但是&#xff0c;聊天机器人创作的论文足以骗过大学招生顾问吗&#xff1f; ChatGPT简介 ChatGPT&#xff0c;全称聊天生…

C++ 之LeetCode刷题记录(二十)

&#x1f604;&#x1f60a;&#x1f606;&#x1f603;&#x1f604;&#x1f60a;&#x1f606;&#x1f603; 开始cpp刷题之旅。 依旧是追求耗时0s的一天。 110. 平衡二叉树 给定一个二叉树&#xff0c;判断它是否是高度平衡的二叉树。 本题中&#xff0c;一棵高度平衡二…

构建外卖跑腿系统:技术实现与架构设计

在当今数字化时代&#xff0c;外卖跑腿系统已成为人们生活中不可或缺的一部分。本文将探讨如何利用先进的技术和架构设计&#xff0c;开发一个高效、可靠的外卖跑腿系统。 1. 技术选型 在开发外卖跑腿系统之前&#xff0c;我们需要仔细选择适合的技术栈&#xff0c;以确保系…

[C++13]:stack queue priority_queue 模拟实现

stack && queue && priority_queue 模拟实现 一.stack1.概念&#xff1a;2.使用&#xff1a;3.模拟实现&#xff1a;一些题目&#xff1a;1.最小栈&#xff1a;2.栈的压入弹出序列&#xff1a;3.逆波兰表达式求值&#xff1a; 二.queue1.概念&#xff1a;2.使用…

SpringBoot之时间数据前端显示格式化

背景 在实际我们通常需要在前端显示对数据操作的时间或者最近的更新时间&#xff0c;如果我们只是简单的使用 LocalDateTime.now()来传入数据不进行任何处理那么我们就会得到非常难看的数据 解决方式&#xff1a; 1). 方式一 在属性上加上注解&#xff0c;对日期进行格式…

Web3 游戏开发者的数据分析指南

作者&#xff1a;lesleyfootprint.network 在竞争激烈的 Web3 游戏行业中&#xff0c;成功不仅仅取决于游戏的发布&#xff0c;还需要在游戏运营过程中有高度的敏锐性&#xff0c;以应对下一次牛市的来临。 人们对 2024 年的游戏行业充满信心。A16Z GAMES 和 GAMES FUND ONE …

探索IOC和DI:解密Spring框架中的依赖注入魔法

IOC与DI的详细解析 IOC详解1 bean的声明2 组件扫描 DI详解 IOC详解 1 bean的声明 IOC控制反转&#xff0c;就是将对象的控制权交给Spring的IOC容器&#xff0c;由IOC容器创建及管理对象。IOC容器创建的对象称为bean对象。 要把某个对象交给IOC容器管理&#xff0c;需要在类上…

深度学习知识

context阶段和generation阶段的不同 context阶段&#xff08;又称 Encoder&#xff09;主要对输入编码&#xff0c;产生 CacheKV(CacheKV 实际上记录的是 Transformer 中 Attention 模块中 Key 和 Value 的值&#xff09;&#xff0c;在计算完 logits 之后会接一个Sampling 采…

【MySQL进阶】InnoDB引擎存储结构和架构

文章目录 逻辑存储结构架构内存结构Buffer Pool&Adaptive Hash IndexChange BufferLog Buffer 磁盘结构 逻辑存储结构 表空间&#xff08;Tablespaces&#xff09;&#xff1a;InnoDB使用表空间来管理存储表和索引的数据文件。每个表空间包含一个或多个数据文件&#xff0c…

【学网攻】 第(9)节 -- 路由器使用以及原理

系列文章目录 目录 系列文章目录 文章目录 前言 一、路由器是什么&#xff1f; 二、实验 1.引入 总结 文章目录 【学网攻】 第(1)节 -- 认识网络【学网攻】 第(2)节 -- 交换机认识及使用【学网攻】 第(3)节 -- 交换机配置聚合端口【学网攻】 第(4)节 -- 交换机划分Vlan…

解锁一些SQL注入的姿势

昨天课堂上布置了要去看一些sql注入的案例&#xff0c;以下是我的心得&#xff1a; ​​​​​​​ ​​​​​​​ ​​​​​​​ 1.新方法 打了sqli的前十关&#xff0c;我发现一般都是联合查询&#xff0c;但是有没有不是联合查询的方法呢&#xf…

Python基础学习 -05 基本类型

Python3 基本数据类型 Python 中的变量不需要声明。每个变量在使用前都必须赋值&#xff0c;变量赋值以后该变量才会被创建。 在 Python 中&#xff0c;变量就是变量&#xff0c;它没有类型&#xff0c;我们所说的"类型"是变量所指的内存中对象的类型。 等号&…

Nodejs前端学习Day1_补档

我给day1搞没了&#xff0c;还是觉得该补一个&#xff0c;有用 文章目录 前言一、学习目标二、学习目录三、为什么JavaScript代码可以在浏览器中运行四、为什么JavaScript可以操作DOM和BOM五、浏览器中的JavaScript运行环境总结 前言 补档 一、学习目标 二、学习目录 三、为什…

苹果备忘录删除了怎么恢复?看这,5分钟学会4种方法

在日常使用中&#xff0c;我们有时会不小心删除苹果备忘录中的重要内容。这些内容可能是重要的提醒、重要的日程安排&#xff0c;也可能是珍贵的回忆。一旦删除&#xff0c;可能会对我们的生活和工作带来很大的困扰。那么&#xff0c;苹果备忘录删除了怎么恢复呢&#xff1f;本…