2.1.2 事件驱动reactor的原理与实现

LINUX 精通 2

day14 20240513
day15 20240514
算法刷题:2维前缀和,一二维差分 耗时 135min
习题课 4h

课程补20240425 耗时:4h

课程链接地址

回顾

  1. 怎么学0voice课
  2. 网络io——一请求一线程,一个client一个连接再accpet分配io fd文件描述符

注意:rm -rf是一个非常强大和危险的命令,它会递归地删除目录及其内容,而不进行任何确认。请谨慎使用此命令,以免意外删除重要文件或系统关键组件

每次上课前统一一下代码

  1. gitlab.0voice.com

    找到代码,然后在Linux终端里 git clone 可以挂梯子, 用户名是邮箱

    在本地下连ftp shell好像不太行

  2. gcc -o networkio networkio.c -lpthread

问题

  1. client断开后问题

    运行以后./networkic 连三个 发三个,然后断开一个,会一直send recv 一瞬间cpu占用100%, 因为没有处理断开的

我没碰到,因为老师改过了void *client_thread加了count==0处理的

if (count == 0) { // disconnectprintf("client disconnect: %d\n", clientfd);close(clientfd);break;}
  1. fd就是网络io 是int型

    开了sockfd在这里插入图片描述

    fd(不论是sockfd 还是clientfd) 从3开始,0 1 2系统默认stdin stdout stderror;往上增加

    ls /dev/fd
    

    在这里插入图片描述

    看ulimit -a文件描述符fd数量max (open files)

    在这里插入图片描述

    为什么能一直增加:

    linux下操作,一切皆是文件FD(file descriptor)

    可以隔段时间再send

  2. client断开后,隔段时间再连接,fd变了吗

    变了

    disconnect以后就 close(clientfd)了!!!!

    4没了,被回收了,变成了7

    等一段时间,又变成4了

    io回收时间 系统默认60s,set可以设置time_wait

2.1.2 事件驱动reactor的原理与实现

还是用我自己的版本

一请求一线程

优点:代码逻辑简单

缺点:不利于并发 1k,通过创建线程实现并发

所以用多路复用io

调试技巧

在命令行打man select函数名 就能看解释

select poll epoll

fdset
  1. 到底是什么东西?

    1. 答案:它是比特位集合

      把fd放一起的set集合

      干嘛的:比如你时间管理大师,处了好多个fd 对象

    2. 为什么要设置一个集合fdset, 然后传进select,传来传去

      传入3456,系统返回34可读

    在这里插入图片描述

    ​ 所有通讯底层的server io多路复用都是这么写的

    ​ 云里雾里的头痛,乱七八糟的,send可以,但是收不到!!!!我 爆炸了裂开

    现在是main里一个线程,多路io fd,fd间不影响!!

    ​ 可读返回,不可读阻塞在select,对着标准答案改了终于行了

    1. fd_set结构体

      select头文件里就1个struct, 4个宏定义,1个函数不难的

      宏定义FD_ZERO, FD_SET, FD_CLR,

      select()函数的参数:可读 可写,错误,

      timeout=null 默认一直阻塞, 如果阻塞超过市场就往下走,可以做一个定时器,就是为了切换线程,等待就绪再次被执行

  2. 大小

    fd_setsize大小可以改

    在posix_types.h里看到

    在这里插入图片描述

    fd_setsize = 1024

    8因为一个字节byte = 8bit

    sizeof(long)因为前面是unsigned long 所以要除它, 假设long是4 byte

    所以一个fd大小是1024/(8*4) = 32 byte

select

特点/运行机制

  1. 每次调用select需要把fd_set集合,从用户空间copy到kernel内核空间

  2. maxfd, 为了遍历fd是否set置一了,设置的最大的fd,需要人为设置

    for( int i = 0; i< maxfd; i ++)

    ps:rfds, rset区别

fd_set rfds, rset; 
//rfds返回数据dataset, 是应用层的,用户设置的
// rset是复制rfds的, 用于被复制到内核空间,用于判断的

优点:实现只用一个thread进程就能多路io复用

缺点: 参数太多,麻烦

 
#elsefd_set rfds, rset; //rfds返回数据dataset, 是应用层的,用户设置的// rset是复制rfds的, 用于被复制到内核空间,用于判断的FD_ZERO(&rfds); //先清空FD_SET(sockfd, &rfds); // 再把sockfd 设置在可读rfds里,置1int maxfd = sockfd; //用来方便遍历set用到的最大值while(1){rset = rfds; //关联,// maxfd +1因为下标从0开始,数量比最后多1//  int select(int nfds, fd_set *readfds, fd_set *writefds,//   fd_set *exceptfds, struct timeval *timeout);int nready = select(maxfd + 1, &rset, NULL, NULL, NULL); //返回就绪的fd数量,就绪的bit位是1if(FD_ISSET(sockfd, &rset)){//sockfd位是否置1// accept 如果监听的sockfd置1了,就开始accept连接int clientfd = accept(sockfd, (struct sockaddr*)&clientaddr, &len);printf("accept finished: %d\n", clientfd);FD_SET(clientfd, &rfds); //有一个fd,就set一下,maxfd也变了if(maxfd < clientfd) maxfd = clientfd; //clientfd当client断连了,会被回收,所以要判断一下}// recvint i = 0;for(i = sockfd +1; i<= maxfd; i++){ //i就是fdif (FD_ISSET(i, &rset)){//判i可读rset吗char buffer[1024] = {0};int count = recv(i, buffer, 1024, 0); //Read N bytes into BUF from socket FD.if (count == 0) { // disconnectprintf("client disconnect: %d\n", i);close(i);FD_CLR(i, &rfds); //FD_CLR是一个宏,用于从fd_set数据结构中清除指定的文件描述符continue;}printf("RECV: %s\n", buffer);count = send(i, buffer, count, 0);printf("SEND: %d\n", count);}}}#endif
poll
  1. struct pollfd里

    比如上面传入3456 只返回34可读

    fd 是哪一个fd

    events 传入的事件

    revents 返回的事件

    基本代码和select差不多,就是4个宏还有select函数要改成poll才有的 写法

    别进错文件夹编译

    gcc -o networkio networkio.c
    ./networkio
    

    开三个网络助手——connect——send——recv吗

    在这里插入图片描述

    成功啦啦啦啦啦啦啦

  2. 总结poll有什么呢

    1. pollfd是个结构体:fd 是哪一个fd;events 传入的事件;revents 返回的事件

    2. 宏定义:pollin可读,pollout等等

    3. poll函数是系统调用 每次把fds copy到内核kernel里

      系统用for遍历maxfd个数量,判断这个io fd是否就绪

  3. poll有什么独特使用场景

    1. 底层逻辑类似select参数更少

    2. 问: 假设5个fd一起来,阻塞,假设都不能可读一直等,直到1可读立即返回?

      答:不对,因为内核做不了微秒级,第一次与第二次进while的select往下几乎没有处理上的差别

      没懂???

#else//  进pollfd 看参数struct pollfd fds[1024]= {0}; //fdset就是fds[sockfd].fd = sockfd;fds[sockfd].events= POLLIN; //pollin就是可读,设置为POLLIN表示对该文件描述符上是否有可读数据感兴趣int maxfd = sockfd; //来遍历用的,检查哪个fd set了 while(1){int nread = poll(fds, maxfd + 1, -1);//set, set大小, timeout =-1一直阻塞等待if (fds[sockfd].revents & POLLIN){// pollin是x十六进制0x0001,变成8位2进制00000001// 如果有可读的,用accept处理分配io,复制上面int clientfd = accept(sockfd, (struct sockaddr*)&clientaddr, &len);printf("accept finished: %d\n", clientfd);// FD_SET(clientfd, &rfds); //select的这句改成poll的下面两行fds[clientfd].fd= clientfd;fds[clientfd].events=POLLIN;if(maxfd < clientfd) maxfd = clientfd; //clientfd当client断连了,会被回收,所以要判断一下}// 抄上面recvint i = 0;for(i = sockfd +1; i<= maxfd; i++){ //i就是fd// if (FD_ISSET(i, &rset)){//判i可读rset吗 select有,poll没有if(fds[i].revents & POLLIN){ //判i可读吗,和Pollin位与char buffer[1024] = {0};int count = recv(i, buffer, 1024, 0); //Read N bytes into BUF from socket FD.if (count == 0) { // disconnectprintf("client disconnect: %d\n", i);close(i);// FD_CLR(i, &rfds); //FD_CLR是fdset里的一个宏,select有;poll没有fds[i].fd= -1; //因为从0开始,置-1fds[i].events= 0;continue;}printf("RECV: %s\n", buffer);count = send(i, buffer, count, 0);printf("SEND: %d\n", count);}}}
epoll

linux 2.4以前,没有听过linux做server的,也没有云主机。当时server都是Windows,unix,十几年后现在云主机很多系统都是Linux,因为linux2.6以后引入epoll,server对io的数量更多

为什么?select与poll底层都需要进while的select/poll阻塞检查,再for判断 accept recv ,epoll不用

#elseint epfd = epoll_create(1);struct epoll_event ev; //构建事件,只用来add和delete,control里没用ev.events = EPOLLIN;ev.data.fd = sockfd;epoll_ctl(epfd, EPOLL_CTL_ADD, sockfd, &ev); //controlwhile(1){struct epoll_event events[1024] = {0};int nready = epoll_wait(epfd, events, 1024, -1);int i = 0;for (i = 0;i < nready;i ++) {int connfd = events[i].data.fd;if (connfd == sockfd) {// accept				int clientfd = accept(sockfd, (struct sockaddr*)&clientaddr, &len);printf("accept finshed: %d\n", clientfd);// 创建events, 添到ctl里ev.events = EPOLLIN;ev.data.fd = clientfd;// 这里ev不写也可以epoll_ctl(epfd, EPOLL_CTL_ADD, clientfd, &ev);} else if (events[i].events & EPOLLIN) {char buffer[1024] = {0};int count = recv(connfd, buffer, 1024, 0);if (count == 0) { // disconnectprintf("client disconnect: %d\n", connfd);close(connfd);// 改了这里epoll_ctl(epfd, EPOLL_CTL_DEL, connfd, NULL);// epoll_ctl(epfd, EPOLL_CTL_DEL, connfd, &ev); 也可以continue;}printf("RECV: %s\n", buffer);count = send(connfd, buffer, count, 0);printf("SEND: %d\n", count);}}}

总结

  1. 一个struct结构体+三个函数给应用层提供的接口

    在这里插入图片描述

  2. int epoll_create(int size);知识点1:epoll_create返回传入都是int。

    epoll一开始size代表一次性就绪的io数量,后来就绪从数组改成链表,此时size没作用只要size不为0,效果都一样。 size为了兼容老版本留下来了

  3. 为什么epoll不用遍历?

    住户是IO或者fd,epoll类似快递员,但是去丰巢,由io自己去丰巢,事件events 有两个= 收+寄;poll、select是每家敲门,没法时时敲门还

    在这里插入图片描述

    1. epoll_create:用struct组织200总集200个住户IO;用struct组织快递柜丰巢——就绪,并聘请快递员
    2. epoll_ctl: 住户搬走EPOLL_CTL_DELET,搬进ADD,换楼层位置MOD
    3. epoll_wait: 快递员多久去一次丰巢,timeout市场;events是小车取出就绪内容,maxevent是小车多大
  4. 为什么能大并发?

    1. select(maxfd, rfds, wfds, efds, err);五个参数

      if 100万 io,需要把100万全部copy到对应fds里判断可读、写…

    2. 但是epoll不用每次从用户应用层copy到内核里,epoll create是一个个添加到内核里积累起来,有读写事件来了,wait就从就绪里操作

    3. 就绪里是真正处理的事件:微信号称3亿用户同时在线,就是IO整集大小,但是不代表server处理同时发消息

      每个client对应io,可能就绪队列在发消息的才一百万不到

  5. 思考题:

    1. 整集用什么数据结构存
      • 数组:将整数按顺序存储在数组中,并通过索引访问。这种方式简单直接,但插入和删除操作的时间复杂度较高。
      • 链表:将每个整数存储在链表节点中,并使用指针连接节点。这种方式对于频繁的插入和删除操作更为高效,但随机访问的性能较差。
      • 哈希集合:利用哈希函数将整数映射到不同的桶中,在每个桶内使用链表或红黑树来处理冲突。这种方式可以实现快速查找、插入和删除操作。
    2. 就绪用什么数据结构存
      • 位图(Bitmask):使用一个二进制位代表一个文件描述符,当某个文件描述符就绪时,相应位设置为1。该方法适用于文件描述符数量较少且连续排列的情况。
      • 数组:将就绪的文件描述符存储在数组中。该方法适用于文件描述符数量不多且无需频繁变动的情况。
      • 数据结构依赖具体的多路复用机制:例如,epoll使用红黑树来管理就绪事件,将文件描述符作为节点进行存储;而select和poll则使用fd_set结构体数组来存储就绪文件描述符。

引出reactor反应堆

  1. epoll:io数量很多;

    poll:io少, <10

    select: 无poll epoll 比如Linux2.4前

  2. 都是对网络应用层的IO事件处理4种,根本没有对用户层的业务service处理还,只处理了IO里的事件,告诉你吃饭events事件了,没告诉你什么饭怎么吃具体的service

  3. 过程:server listen——client connect——server accept——client send ——server receive同时回传

  4. 事件:

    所以是IO事件触发——水平触发,边沿触发

    核心是events事件,一个IO的生命周期=无数多个events

    server关心events,不是具体io,对不同events执行不同callback回调函数cb,模块化!!!

  5. reactor:核反应堆=不同IO事件处理callback回调函数 cb的集合

    封装起来,给以后用户层service用,更符合人的逻辑

    **什么是reactor:**注册一个events,当events发生,就从reactor查返回回调函数,做反应(类似留下名片只要有事就call 你)

    为什么要用reactor因为可以更好关注事件而不是io,io太多也不是都活着,从io管理➡️ 事件管理

网络IO

  1. accept——》listenfd/sockfd
  2. send/recv——》clientfd

网络应用是所有服务的基石

ps:

要用形象的东西,记忆更牢,也不反人性,少掉头发少烧脑

5/15晚上把github同步自己代码搞定!!!

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

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

相关文章

曲线救国:window 安装 docker

你好&#xff0c;我是 shengjk1&#xff0c;多年大厂经验&#xff0c;努力构建 通俗易懂的、好玩的编程语言教程。 欢迎关注&#xff01;你会有如下收益&#xff1a; 了解大厂经验拥有和大厂相匹配的技术等 希望看什么&#xff0c;评论或者私信告诉我&#xff01; 文章目录 一…

软件3班20240513

java.util.PropertyResourceBundle4554617c package com.yanyu;import java.sql.*; import java.util.ResourceBundle;public class JDBCTest01 {public static void main(String[] args) throws SQLException { // 获取属性配置文件ResourceBundle bundle Res…

亚马逊是如何铺设多个IP账号实现销量大卖的?

一、针对亚马逊平台机制&#xff0c;如何转变思路&#xff1f; 众所周知&#xff0c;一个亚马逊卖家只能够开一个账号&#xff0c;一家店铺&#xff0c;这是亚马逊平台明确规定的。平台如此严格限定&#xff0c;为的就是保护卖家&#xff0c;防止卖家重复铺货销售相同的产品&a…

ICode国际青少年编程竞赛- Python-5级训练场-综合练习5

ICode国际青少年编程竞赛- Python-5级训练场-综合练习5 1、 a 16 for i in range(6):Dev.step(1)Dev.turnLeft()Dev.step(a)Dev.step(-a)Dev.turnRight()while Dev.energy < 100:wait()Dev.step(1)a a - 5 i2、 for i in range(5):Dev.step(11 - i * 2)Dev.turnRight()wh…

考研数学|李林《880》做不动,怎么办!?看这一篇!

在考研数学的备考过程中&#xff0c;遇到难题是很常见的情况&#xff0c;尤其是当你尝试解决李林880习题集中的问题时。他以其难度和深度著称&#xff0c;旨在帮助考生深入理解数学分析的复杂概念。 如果你在解题过程中感到困难&#xff0c;这并不是你个人的问题&#xff0c;而…

【八大排序算法】插入排序、希尔排序、选择排序、堆排序、冒泡排序、快速排序、归并排序、计数排序

文章目录 一、排序的相关概念二、排序类型三、排序算法实现插入排序1.直接插入排序2.希尔排序 选择排序3.简单选择排序4.堆排序 交换排序5.冒泡排序6.快速排序递归实现非递归实现 7.归并排序递归实现非递归实现 8.计数排序 四、总结 一、排序的相关概念 排序&#xff1a;根据数…

MapReduce代码

WordCount 数据准备&#xff1a; a.txt lxy lxy lxy zhang wsoossj liagn guui liang liagn代码&#xff08;在idea中创建一个Maven工程&#xff09;&#xff1a; mapper&#xff1a; package com.lxy.mr.wordcount.thi;import org.apache.hadoop.io.LongWritable; import…

【经验总结】超算互联网服务器 transformers 加载本地模型

1. 背景 使用 超算互联网 的云服务&#xff0c;不能连接外网&#xff0c;只能把模型下载到本地&#xff0c;再上传上去到云服务。 2. 模型下载 在 模型中 https://huggingface.co/models 找到所需的模型后 点击下载 config.json pytorch_model.bin vocab.txt 3. 上传模型文…

矩阵相关运算1

矩阵运算是线性代数中的一个核心部分&#xff0c;它包含了许多不同类型的操作&#xff0c;可以应用于各种科学和工程问题中。 矩阵加法和减法 矩阵加法和减法需要两个矩阵具有相同的维度。操作是逐元素进行的&#xff1a; CAB or CA−B其中 A,B 和 C 是矩阵&#xff0c;且 C…

idea控制台日志控制

1.清除控制台log日志 测试的时候&#xff0c;控制台打印的日志比较多&#xff0c;速度有点慢而且不利于查看运行结果&#xff0c;所以接下来我们把这个日志处理下: 取消初始化spring日志打印&#xff0c;resources目录下添加logback.xml&#xff0c;名称固定&#xff0c;内容如…

智能自助终端主板RK3288/RK3568在酒店前台自助机方案的应用,支持鸿蒙,支持免费定制

酒店前台自助机解决方案是一款基于自助服务终端&#xff0c;能够让客人通过简单的操作完成入住登记/退房的解决方案&#xff0c;大幅提高酒店的工作效率&#xff0c;提升客人体验&#xff0c;降低人力成本。 该方案解决了以下传统前台登记入住方式的痛点&#xff1a; 1、人流量…

过滤器Filter和拦截器Interceptor实现登录校验

一.过滤器 Filter过滤器可以把对资源的请求拦截下来&#xff0c;从而实现一些登录验证的功能 1.Filter的快速入门 1.定义Filter:定义一个类&#xff0c;实现Filter接口&#xff0c;并重写其所有方法。2.配置 public class dofilter implements Filter {Override //初始化只…

Git使用(3):版本管理

一、查看历史 编写一个java类进行测试 选择Git -> Show Git Log查看日志。 第一次修改推送到远程仓库了&#xff0c;所以有origin&#xff08;远程仓库地址&#xff09;&#xff0c;第二次修改只提交到本地仓库所以没有。 二、版本回退 1、本地回退 在要回退的版本上右键&a…

线程纵横:C++并发编程的深度解析与实践

hello &#xff01;大家好呀&#xff01; 欢迎大家来到我的Linux高性能服务器编程系列之《线程纵横&#xff1a;C并发编程的深度解析与实践》&#xff0c;在这篇文章中&#xff0c;你将会学习到C新特性&#xff0c;并发编程&#xff0c;以及其如何带来的高性能的魅力&#xff0…

linux系统修改网卡名称

说明&#xff1a; 因操作过程需要停用网卡&#xff0c;导致ssh远程连接不上&#xff0c;需要控制台登录操作。 测试环境&#xff1a; CentOS7.9、8.2虚拟机 Suse15 SP4虚拟机 操作步骤&#xff1a; 方法一&#xff1a; 1、 查看网卡当前名称及状态 ip a2、 将网卡状态从启用…

uni-app(三):离线打包与插件引用(Android)

离线打包与插件引用 1.下载Android离线SDK2.使用Android Studio打开离线打包项目并更新Gradle3.解决报错4.构建5.配置AppKeya.查看证书b.申请AppKeyc.配置AppKey 6.生成本地打包App资源7.拷贝App资源到Android项目中8.修改 appid9.修改Android项目配置文件10.下载证书并配置11.…

如何将一个流固耦合的爆炸案例修改成一个没有固体的爆炸案例(类似于blastfoam的twochargeDetonation案例,可以重点模仿这个来)

t技巧总结&#xff1a;~/myapp/OpenFOAM-7/blastfoam_2_0/tutorials/twoChargeDetonation案例对比&#xff0c;发现确实这个案例也没有固体文件夹和precice-config文件夹&#xff0c;只需要用到openfoam7与blastfoam2.0.0。&#xff08;这个案例可以当做一个很好的爆炸案例的入…

51. UE5 RPG 自定义FGameplayEffectContext

我们期望能够通过FGameplayEffectContext将此次技能造成的伤害是否触发格挡和暴击的参数传递到AttributeSet中&#xff0c;所以需要实现自定义一个FGameplayEffectContext类&#xff0c;来增加对应的配置。 创建自定义类文件 首先在Public目录上右键&#xff0c;选择添加一个…

【Flutter】极光推送配置流程(VIVO/OPPO/荣耀厂商通道) 章三

相关文章 推送配置共三篇(如下链接) 【Flutter】极光推送配置流程(极光通道/华为厂商/IOS) 章一 【Flutter】极光推送配置流程(小米厂商通道) 章二 【Flutter】极光推送配置流程(VIVO/OPPO/荣耀厂商通道) 章三 前言 很高兴大家来看小编写的文章&#xff5e;&#xff5e; 继【…

NSSCTF中的web学习(md5())

目录 MD5的学习 [BJDCTF 2020]easy_md5 [LitCTF 2023]Follow me and hack me [LitCTF 2023]Ping [SWPUCTF 2021 新生赛]easyupload3.0 [NSSCTF 2022 Spring Recruit]babyphp MD5的学习 md5()函数&#xff1a; md5($a)&#xff1a;返回a字符串的散列值 md5($a,TRUE)&…