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.创建SelectorSelector selector = Selector.open();// 2.获得ChannelServerSocketChannel 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();// serverSocketChannelServerSocketChannel serverSocketChannel = ServerSocketChannel.open();serverSocketChannel.configureBlocking(false);// 绑定端口serverSocketChannel.bind(new InetSocketAddress(9001));// 注册SelectionKey selectionKey = serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);// selector轮询while (true){// 阻塞等待某个操作就绪状态的channelselector.select();// 获得一个集合,里面包含了这次selector执行select方法获得的发生就绪状态的多个channelSet<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 {// 获得ChannelServerSocketChannel serverSocketChannel = ServerSocketChannel.open();// 设置成非阻塞serverSocketChannel.configureBlocking(false);// 绑定端口号serverSocketChannel.bind(new InetSocketAddress(9001));// 获得SelectorSelector selector = Selector.open();// 把channel注册到selector上面serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);// 让selector轮询监听while (true){// 阻塞直到有通道就绪selector.select();Set<SelectionKey> selectionKeys = selector.selectedKeys();// 获取有动作的selectionKey == channelIterator<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中的channelServerSocketChannel serverSocketChannel = (ServerSocketChannel)selectionKey.channel();// 接收客户端连接,获得socketChannelSocketChannel 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);// 创建BufferByteBuffer 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 {// 创建ChannelSocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("localhost", 9001));// 设置成非阻塞模式socketChannel.configureBlocking(false);// 得到bufferByteBuffer buffer = ByteBuffer.allocate(1024);// 把数据写入到buffer中buffer.put("hello selector".getBytes());// 反转bufferbuffer.flip();// 把buffer中的数据写入到channel中socketChannel.write(buffer);// 关闭socketChannel.close();}}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Spring boot + Azure OpenAI 服务 1.使用 GPT-35-Turbo

Azure OpenAI 服务使用 GPT-35-Turbo 先决条件 maven 注意 beta.6 版本 <dependency><groupId>com.azure</groupId><artifactId>azure-ai-openai</artifactId><version>1.0.0-beta.6</version></dependency>问答工具类 pack…

C++的关键字,命名空间,缺省参数,函数重载以及原理

文章目录 前言一、C关键字(C98)二、命名空间命名空间介绍命名空间的使用 三、C输入【cin】& 输出【cout】四、缺省参数缺省参数概念缺省参数分类缺省参数的使用小结一下 五、函数重载函数重载介绍函数重载类型 六、C支持函数重载的原理--名字修饰(name Mangling)【重点】 前…

二分算法模版

二分算法模版 实数二分算法模版实数二分模版题 整数二分算法模版向上取整二分模版向下取整二分模版二分模版的注意点二分模版中check函数的实现能够使用二分的条件 二分主要分两类&#xff0c; 一类是对实数进行二分&#xff0c;一类是对整数进行二分 对整数二分又分成2种&…

python-自动化篇-运维-监控-简单实例-道出如何使⽤Python进⾏系统监控?

如何使⽤Python进⾏系统监控&#xff1f; 使⽤Python进⾏系统监控涉及以下⼀般步骤&#xff1a; 选择监控指标&#xff1a; ⾸先&#xff0c;确定希望监控的系统指标&#xff0c;这可以包括 CPU 利⽤率、内存使⽤情况、磁盘空间、⽹络流量、服务可⽤性等。选择监控⼯具&#x…

react如果创建了类似于 Icketang元素,那么该如何实现 Icketang类

要实现一个类似于 "Icketang" 的类&#xff0c;首先需要考虑该类的属性和方法。根据上下文&#xff0c;可以假设 "Icketang" 是一个卡片或票据类&#xff0c;可以包含以下属性和方法&#xff1a; 属性&#xff1a; card_number&#xff1a;卡片编号amoun…

Java实现加权平均分计算程序WeightedAverageCalculator

成绩加权平均分计算程序&#xff0c;带UI界面和输入保存功能。 因为本人对成绩的加权均分有所关注&#xff0c;但学校的教务系统查分时往往又不显示个人的加权均分&#xff0c;加之每次手动敲计算器计算很麻烦就花了点时间写了一个加权均分计算程序自用&#xff0c;顺便开源。…

STM32标准库——(5)EXTI外部中断

1.中断系统 中断&#xff1a;在主程序运行过程中&#xff0c;出现了特定的中断触发条件&#xff08;中断源&#xff09;&#xff0c;使得CPU暂停当前正在运行的程序&#xff0c;转而去处理中断程序&#xff0c;处理完成后又返回原来被暂停的位置继续运行 中断优先级&#xff…

《WebKit 技术内幕》学习之十五(6):Web前端的未来

6 Chromium OS和Chrome的Web应用 6.1 基本原理 HTML5技术已经不仅仅用来编写网页了&#xff0c;也可以用来实现Web应用。传统的操作系统支持本地应用&#xff0c;那么是否可以有专门的操作系统来支持Web应用呢&#xff1f;当然&#xff0c;现在已经有众多基于Web的操作系统&…

uniapp小程序:内存超过2mb解决方法(简单)message:Error: 上传失败:网络请求错误 代码包大小超过限制。

分析&#xff1a;这种情况是代码文件内存超过2mb无法进行预览上传 解决方法&#xff1a; 1、Hbuilder中点击运行-->运行到小程序模拟器--->运行时是否压缩代码 2、在微信小程序中点击详情--->本地设置&#xff1a; 3、点击预览即可运行了

两个近期的计算机领域国际学术会议(软件工程、计算机安全):欢迎投稿

近期&#xff0c;受邀担任两个国际学术会议的Special session共同主席及程序委员会成员&#xff08;TPC member&#xff09;&#xff0c;欢迎广大学界同行踊跃投稿&#xff0c;分享最新研究成果。期待这个夏天能够在夏威夷檀香山或者加利福尼亚圣荷西与各位学者深入交流。 SERA…

南方故乡吹来的风

故乡的风 - 张明敏 词&#xff1a;刘因国 曲&#xff1a;刘因国 南方故乡吹来的风 带着潮水的呼唤 吹着你的秀发 飘散着茉莉的香 茉莉的香哟 南方故乡吹来的风 带着渔船的归航 吹着你的欢畅 吹着渔帆点点醉哟 点点的醉哟 远方的姑娘 你是否听见 我的心在嘿哟 你…

上位机图像处理和嵌入式模块部署(c/c++ opencv)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 opencv可以运行在多个平台上面&#xff0c;当然windows平台也不意外。目前来说&#xff0c;opencv使用已经非常方便了&#xff0c;如果不想自己编译…

81 C++对象模型探索。数据语义学 - 静态成员变量的存取,非静态成员变量的存取

一&#xff0c;静态成员变量的存取 静态成员变量只有一个实体&#xff0c;保存在可执行文件的数据段中&#xff0c;如果没有初始化则保存在数据段的BBS中&#xff0c;由于存储在执行文件的数据段中&#xff0c;因此在编译阶段就会确定地址。当程序编译完成后&#xff0c;不管运…

20240127如何在线识别德语字幕?

20240127如何在线识别德语字幕&#xff1f; 2024/1/27 11:42 1945[科尔贝格]Kolberg 01:48:49 接近109分钟 德语视频的字幕OCR适配&#xff1a; 1、whisper&#xff0c;8:39-8:58&#xff0c;使用GTX1080需要接近20分钟。对整机性能要求比较重&#xff0c;特别吃显卡&#xff…

LabVIEW信号时间间隔测量

用LabVIEW软件平台开发一个用于测量两路信号时间间隔的系统。系统利用LabVIEW的数据采集和处理能力&#xff0c;能够准确测量并分析来自不同硬件板卡的信号时间间隔&#xff0c;这对于精确控制和数据分析至关重要。 系统主要由以下几部分组成&#xff1a;数据采集卡、信号处理…

MySQL:事务隔离级别详解

事务一共有四个特性&#xff1a;原子性、隔离性、持久性、一致性。简称ACID。本文所将就是其中的隔离性。 1、事务中因为隔离原因导致的并发问题有哪些&#xff1f; 脏读&#xff1a;当事务A对一个数据进行修改&#xff0c;但这个操作还未提交&#xff0c;但此时事务B就已经读…

ArrayList集合简单源码分析+一道面试题

ArrayList类中的属性 public class ArrayList<E> extends AbstractList<E>implements List<E>, RandomAccess, Cloneable, java.io.Serializable {java.io.Serialprivate static final long serialVersionUID 8683452581122892189L;/*** DEFAULT_CAPACITY表…

【ASP.NET Core 基础知识】--数据库连接--数据迁移和代码优先开发

一、数据迁移 1.1 定义和用途 数据迁移是指将数据从一个存储系统、数据格式、应用程序或硬件平台转移到另一个的过程。这个过程可以涉及数据的转换、清洗和验证&#xff0c;以确保数据的完整性和一致性。一般用于如下情况&#xff1a; 系统升级&#xff1a; 当企业需要更新其…

力扣(LeetCode)227. 基本计算器 II

给你一个字符串表达式 s &#xff0c;请你实现一个基本计算器来计算并返回它的值。 整数除法仅保留整数部分。 你可以假设给定的表达式总是有效的。所有中间结果将在 [-231, 231 - 1] 的范围内。 注意&#xff1a;不允许使用任何将字符串作为数学表达式计算的内置函数&#…

第二百八十九回

文章目录 1. 概念介绍2. 方法与细节2.1 实现方法2.2 具体细节 3. 示例代码4. 内容总结 我们在上一章回中介绍了"如何混合选择多个图片和视频文件"相关的内容&#xff0c;本章回中将介绍如何通过相机获取视频文件.闲话休提&#xff0c;让我们一起Talk Flutter吧。 1. …