NIO-Selector 网络编程

news2024/11/16 17:33:28

目录

一、阻塞 & 非阻塞

1、阻塞

2、非阻塞

二、selector

1、连接和读取

2、处理客户端断开

3、处理消息的边界

4、ByteBuffer大小分配

三、多线程优化

四、NIO vs BIO

1、stream vs channnel

2、IO模型

阻塞IO

非阻塞IO

多路复用

异步IO模型


一、阻塞 & 非阻塞

1、阻塞

服务器端的代码

然后创建客户端,直接连服务器端的代码就可以了

运行发现服务端线程执行到accept这个方法后就停止运行了,这个方法是阻塞的,要等连接建立完成之后才能继续运行;建立完连接后走到read方法又会阻塞等待读入数据,读到数据之后才能恢复运行。

当有多个客户端的时候,服务端在执行到一个客户端的堵塞方法的时候会一直等待,其他客户端就不会被处理。 

2、非阻塞

我们可以把ServerSocoketChannel和SocketChannel改成非阻塞模式,这样如果没有连接直接返回null,没有读取到数据就返回0,不会去堵塞等待了。

但是这样也会有缺点,因为线程是一直循环检查有没有链接和数据的,如果一直没有连接和数据来,线程依然会不断循环,太过繁忙了,所以这种非阻塞模式在开发中也不常用。

二、selector

为了解决上面非堵塞的不断循环问题,我们就引入selector,接下里看看selector的使用

1、连接和读取

用的时候我们要先调用静态open方法创建Selector对象,然后来管理这些channel,channel调用register方法注册到selector,返回值是个SelectionKey对象, SelectionKey就是将来事件发生后,通过它可以知道事件是哪个channel的事件,然后调用interstOps设置对哪个事件感兴趣

channel事件有4中类型:

  • accept-有连接请求的时候触发
  • connect-客户端连接建立后触发
  • read-数据可读时
  • write-可写事件

然后监听的时候用select()方法,没有时间发生的时候线程堵塞,有事件才执行,然后遍历集合调用selectedKey方法处理事件。

如果我们遍历出来不对事件处理,那么下次遍历的时候还会有,也就是说如果有未处理事件,那么是不会阻塞的,如果不想处理的话,我们可以用cannel()方法来取消处理

socket处理的时候要想不去不断循环读取,那么也要交给selector管理,所以这个socketChannel也要调用register方法注册到selector上,还有感兴趣关注的事件

这里要把监听到的事件类型分开处理

这个代码在运行的时候缺空指针了,为什么呢?

selector的集合和selectedKeys集合中每次监听事件中的内容执行完是不会自己删除的,所以我们第一次执行完第二次遍历到有元素执行,建立连接的时候却没有需要建立的返回null,这个时候就抛出空指针了,我们每次执行完一个事件就得手动从容器删除

2、处理客户端断开

我们客户端和服务器连接之后,如果客户端退出会抛出异常,如果我们直接try catch去抓的话会循环,因为客户端关闭会引发read事假,这个事假一直不处理就会一直执行,所以我们必须在catch里面去把key调用cancel()方法取消处理事件

但是如果是正常断开呢,还是会循环,因为正常断开执行不到catch语句不会cancel,就会一直循环处理close这个read事件,所以我们要用read的返回值来区分是不是close指令,是就取消处理掉

3、处理消息的边界

看看下面这种边界问题:

我们用分割符的方法来解决边界的问题,遇到分隔符就说明读取结束了,我们就分割成一个消息

如果我们实际的内容超过了byteBuffer,一次没有读取完不会报错,第二个会把剩下的部分发过来会当成一个完整的消息处理,所以当空间不够的时候,我们需要扩容,而且我们不能把byteBuffer做为局部变量,要当共享的第一个和第二次共用一个byteBuffer。扩容我们是重新搞个更大的buffer2,然后第一次复制过去,第二次的直接读过去

1、我们先修正ByteBuffer不能为局部变量,但是我们不能直接放最外层,因为如果放最外层就变成多个socketChannel公用一个ByteBuffer,这样就乱了,应该每个channel都有自己的byteBuffer,这里就要用到attachment附件,我们可以在注册的时候把buffer当成附件(Object)一起注册为SelectionKey里面。后面要用的时候直接用key调用attachment方法就能获取到buffer

 2、扩容的时候就是比较如果压缩后的长度和限制最大的长度相等,说明这个消息超过了要扩容,压缩就是每次读取完一个分割符后都要进行的,这个压缩方法就是把因为读取的去除

4、ByteBuffer大小分配

每个channel都需要记录可能被分割的消息,因为byteBuffer不能被多个channel共同使用,因此需要每个channel维护一个独立的byteBuffer

ByteBuffer不能太大,太大的话很多连接就需要很大内存了,因此需要设计大小可变的byteBuffer。一种思路是首先分配一个小buffer,比如4k,如果不够再扩容为8k,用新的替换掉旧的,优点是消息连续容易处理,缺点是数据拷贝耗性能。另一个思路是多个数组组成buffer,一个数组不够,把多出来的内容写入新的数组,与前面的区别是消息存储不连续解析复杂,优点是避免了拷贝引起的性能损耗。

三、多线程优化

现在的处理都是单线程的,没有充分利用多核cpu,

我们可以向下图这样优化,分为boss好worker,boss负责建立连接,而worker负责读写操作,如果连接了,可以直接去worker读,worker里面有一个selector,线程数可以很多很多,不可能一直建立很多worker,所以一个worker可以管理多个channel,多个thread,相当于把任务分担了。

问题: 

执行我们现在是worker-0不断循环执行select方法和boss的register可能会有问题,因为他们是同一个selector管理,又是不同线程的,如果上面执行的selector执行select方法会阻塞,所以下面register就不能执行。

我们可以把上面的worker.registery方法移动到sc.register上一行,这样就有可能会先执行到register,再执行worker.registery()里面的select方法,register不会阻塞直接执行完这样就不会有问题了,因为他们是两个线程,所以可能是先执行的register,执行完又会停留到select方法上阻塞,这个时候如果来了个新的客户端,所以肯定阻塞又出现了那个问题

解决:

我们要想一个办法,参考netty的解决办法,让他们在同一个线程内执行,这样就可以管理执行顺序了。

我们在搞个队列,然后注册的方法里像队列中添加一个注册的任务

 

这样我们就能保证是worker-0在执行任务的时候,就会去注册了,就能保证在worker-0来执行这两个事件了,实现两个线程的事件用一个线程执行,这样就可以控制顺序。最后要注意,添加完事件之后要唤醒selector因为那边的selector是一直阻塞状态的

回顾下整体流程:

我们先接收连接,调用worker进行register去初始化,如果是第一次就会把selector线程都创建好,如果是第二次,都会向队列加入任务,这个worker-0的run进来是select就是阻塞住如果没有的话,然后队列加入完任务就会唤醒这个阻塞的worker执行任务,继续他执行完又select没任务阻塞了。

四、NIO vs BIO

1、stream vs channnel

  • stream不会自动缓冲数据,channel会利用系统提供的发送缓冲区,接收缓冲区(更底层)
  • stream仅支持阻塞API,channel同时支持阻塞、非阻塞API,网络channel可配合selector实现多路复用
  • 二者均为全双工,即读写可以同时进行

2、IO模型

当调用channel的read方法或stream的read后,会由用户态切换至操作系统内核态来完成真正的数据读取,而读取又分为两个阶段:等待数据阶段复制阶段

阻塞IO

当调用read切换内核态后,如果没有数据过来,就会等待数据,等数据到了之后处理好了,复制数据完成之后才切换回用户态,这个整个过程用户线程是阻塞的

非阻塞IO

用户线程会一直循环切换内核态看看有没有数据,没有就立即返回,然后继续转化,用户线程始终在运行,这就算非阻塞IO,但是正在有数据复制数据的时候还是阻塞的(这种切换太频繁了,可能会影响系统性能)

多路复用

多路复用一上来不是调用read,而是用select等待事件(这个是阻塞的),如果有内核态会告诉他,这个时候再切换过去复制数据(复制的时候还是需要阻塞) 

他们都是阻塞,那比阻塞的优势在哪里呢?看看下面的图就知道了,阻塞IO的时候,如果当read在阻塞等待的时候,其他还有线程要执行别的操作,他必须等待这个read阻塞完成全部完成才执行;而多路复用selector可以检测多个事件,什么事件都可以触发唤醒他运行,也就是他阻塞的时候等的是一批事件不是一个事件。

异步IO模型

  • 同步:线程自己去获取结果(一个线程)
  • 异步:线程自己不去获取结果,而是由其他线程送结果(至少两个线程)

我们先分析一下上面三种是同步还是异步

  • 阻塞IO就是同步的,是用户线程自己发起read,也是自己阻塞等待接受accept的,所以是同步的,所以也叫同步阻塞IO
  • 非阻塞IO也是同步的,他也是线程自己发送自己接受,只不过他是不断循环去发送,同步非阻塞
  • 多路复用也是同步的,他也是自己发送select之类的等待阻塞,有事件了唤醒自己去执行,还是自己发自己阻塞自己做,也是同步的

异步阻塞是怎么样的呢?

他是这样的,客户端线程read后,直接返回一个回调函数和参数,然后会异步启动线程2去等待数据和复制数据,复制完后用回调方法通知结果。 所以也这种就是非阻塞的,请求完直接返回了,所以也叫异步非阻塞

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

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

相关文章

使用OpenCV检测两张图片的关键点并计算关键点的描述子

#include <iostream> #include <opencv2/core/core.hpp> #include <opencv2/features2d/features2d.hpp> #include <opencv2/highgui/highgui.hpp>

每日一博 - 探索代码世界的地图 code iris

文章目录 地址特性安装 code iris如何使用 地址 https://plugins.jetbrains.com/plugin/7324-code-iris 特性 This plugin visualizes the modules, packages and classes of your project. It’s like a UML based “Google Maps” for your Source Code. Code Iris does…

zabbix代理服务器,高可用,监控java,windows,SNMP

zabbix 一&#xff1a;代理服务器1.设置 zabbix 的下载源&#xff0c;安装 zabbix-proxy2.部署数据库&#xff0c;要求 MySQL 5.7 或 Mariadb 10.5 及以上版本2.1.初始化数据库2.2.创建数据库并指定字符集2.3.创建 zabbix 数据库用户并授权 3.导入数据库信息4.修改 zabbix-prox…

前端项目请求天地图地址报错跨域;报错418

原因是因为转义字符&#xff01;&#xff01;&#xff01;&#xff01;&#xff01; 在请求回来的地址中 给他转化一下再次请求就OK了&#xff01;

oracle 使从表中随机取出一行记录数据

select * from (select rownum no, a.* from a where status_code AVAILABLE and id_type MEM and archive_flag N and rownum<1000000 ) where no >1000000-1 for update 随机取一条&#xff0c;锁住记录&#xff0c;操作完archive_flag Y不会再取。 四种解决…

【动态规划算法】第八题:931.下降路径最小和

&#x1f496;作者&#xff1a;小树苗渴望变成参天大树&#x1f388; &#x1f389;作者宣言&#xff1a;认真写好每一篇博客&#x1f4a4; &#x1f38a;作者gitee:gitee✨ &#x1f49e;作者专栏&#xff1a;C语言,数据结构初阶,Linux,C 动态规划算法\&#x1f384; 如 果 你…

PqQt实现对数据库的添加,删除,修改(完整过程演示)

在PyQt中设置的如下的窗口&#xff1a; 其中的图标是通过新建Resource File加入的 images里面的图片可以在这里面取&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1gOgBpW7s-ZWn_5aRoaYLkQ 提取码&#xff1a;jyjy 我们把这个文件取名为res.qrc 资源文件的使用可以…

基于matlab显示城市街区场景中配备立体摄像机的无人机开发视觉SLAM算法(附源码)

一、前言 视觉SLAM是计算摄像机相对于周围环境的位置和方向&#xff0c;同时映射环境的过程。开发可视化 SLAM 算法并评估其在不同条件下的性能是一项具有挑战性的任务。最大的挑战之一是生成相机传感器的地面实况&#xff0c;尤其是在户外环境中。使用仿真可以在各种场景和相…

HarmonyOS学习路之开发篇—数据管理(分布式文件服务)

分布式文件服务概述 分布式文件服务能够为用户设备中的应用程序提供多设备之间的文件共享能力&#xff0c;支持相同帐号下同一应用文件的跨设备访问&#xff0c;应用程序可以不感知文件所在的存储设备&#xff0c;能够在多个设备之间无缝获取文件。 基本概念 分布式文件 分布…

WebGIS 信息系统-Element项目实战

WebGIS 信息系统-Element项目实战 Element的安装OpenLayers的安装采用直接引用的方式配置开发环境下载Vue文件下载Element文件下载OpenLayers文件 Element的安装 在项目的根目录中&#xff0c;首先按下 Shift鼠标右键&#xff0c;在弹出的右键菜单中选择“在此处打开命令行窗口…

OpenCV 的 remap 函数改变图像中像素的位置

#include "opencv2/highgui/highgui.hpp" #include "opencv2/imgproc/imgproc.hpp" #

Spring Boot 中的 @Scheduled 注解是什么,如何使用

Spring Boot 中的 Scheduled 注解是什么&#xff0c;如何使用 引言 Spring Boot 是一个非常流行的 Java Web 开发框架&#xff0c;它提供了很多方便的功能&#xff0c;其中就包括了 Scheduled 注解。本文将会介绍 Scheduled 注解的基本用法&#xff0c;并附上代码示例&#x…

2023 最新「阿里」Java 高级工程师面试高频题

7月份快要过去了&#xff0c;也意味着金九银十快到了&#xff0c;不知道大家有没有发现今年的面试比之前的都难&#xff1b;去年因为疫情的原因压抑住程序员跳槽的想法&#xff0c;再加上现在 IT 行业内卷越来越严重&#xff0c;这两点原因导致今年的招聘市场非常难受&#xff…

【模式识别目标检测】——基于机器视觉的无人机避障RP-YOLOv3实例

目录 引入 一、YOLOv3模型 1、实时目标检测YOLOv3简介 2、改进的实时目标检测模型 二、数据集建立&结果分析 1、数据集建立 2、模型结果分析 三、无人机避障实现 参考文献&#xff1a; 引入 目前对于障碍物的检测整体分为&#xff1a;激光、红外线、超声波、雷达、…

使用FAST方法检测特征点,然后计算这些特征点的ORB描述子,并使用暴力匹配方法找到匹配的特征点

这段代码主要做了以下几件事情: 读取两幅图像使用FAST方法找出图像中的特征点手写ORB方法计算特征点的描述子使用汉明距离(Hamming distance)进行描述子的匹配显示匹配的结果下面我们会逐行解释每一句代码: 包含头文件:这一部分包含了所有需要的库。 ComputeORB 函数:该函…

【验证码逆向专栏】某度滑块、点选、旋转验证码 v1、v2 逆向分析

声明 本文章中所有内容仅供学习交流使用&#xff0c;不用于其他任何目的&#xff0c;不提供完整代码&#xff0c;抓包内容、敏感网址、数据接口等均已做脱敏处理&#xff0c;严禁用于商业用途和非法用途&#xff0c;否则由此产生的一切后果均与作者无关&#xff01; 本文章未…

SSL密钥算法检测工具-sslciphercheck-SSL/TLS Suffers ‘Bar Mitzvah Attack’漏洞

SSL密钥算法检测工具-sslciphercheck-SSL/TLS Suffers ‘Bar Mitzvah Attack’漏洞 文章目录 sslciphercheck漏洞&#xff1a;SSL/TLS Suffers ‘Bar Mitzvah Attack’漏洞漏洞描述漏洞检测修复建议 sslciphercheck 下载&#xff1a;https://github.com/woanware/woanware.git…

Ubuntu vim 以十六进制的形式显示文件内容

Vim的介绍 Vim&#xff08;Vi IMproved&#xff09;是一款开源的文本编辑器&#xff0c;是 Unix 系统中经典的 vi 编辑器的改进版本。它具有强大的功能和高度可定制性&#xff0c;被广泛应用于程序开发、系统管理和文本编辑等领域。 以下是 Vim 的一些主要特点和功能&#xf…

CRC Principle and Implementation Method(Java C)

CRC原理和程序实现方法1_哔哩哔哩_bilibili 其实原理很简单 但是我想了两个小时。。 收获的是原来一些复杂的运算都可以通过位运算来实现。 实现思路 public class CRC16Calculator {public static String CRC16(byte[] bytes) {int CRC 0x0000ffff;int POLYNOMIAL 0x0000a…

【复习3-5天的内容】【我们一起60天准备考研算法面试(大全)-第七天 7/60】

专注 效率 记忆 预习 笔记 复习 做题 欢迎观看我的博客&#xff0c;如有问题交流&#xff0c;欢迎评论区留言&#xff0c;一定尽快回复&#xff01;&#xff08;大家可以去看我的专栏&#xff0c;是所有文章的目录&#xff09;   文章字体风格&#xff1a; 红色文字表示&#…