Reactor

文章目录

    • 正确的理解发送
    • double free问题
      • 解决
  • 1.把我们的reactor进行拆分
  • 2.链接管理
  • 3.Reactor的理论

listensock只需要设置_recv_cb,而其他sock,读,写,异常
所以今天写nullptr其实就不太对,添加为空就没办法去响应事件
在这里插入图片描述
获取新链接并没有对它做任何处理,那么我要对他做处理

Accepter 连接管理器
主要负责处理进行处理所有连接的

还有一批接口是事件管理器
也就是统一进行对所有事件来进行响应

获取了新链接,然后AddConnection把它设置进去 ,
在这里插入图片描述之后已经把对应的新链接添加到内核里了,所以未来再进行事件循环时,一定会帮我们检测到已经就绪的事件,而这个时候就可能会包含普通的事件就绪,
在这里插入图片描述
普通的事件就绪他就会检测是否是读事件还是写事件,连接是否是健康的,然后直接执行曾经绑定的方法,对于普通的文件描述符,他绑定的方法刚好是它曾经设置的回调,也就是刚刚绑定的Recver
只要执行Recver,最终再把自己的connection对象指针传进去,所以未来在Recver这通过connection里面的fd , 此时直接读。

接下来的工作就是处理读了
你知道怎么处理数据吗?
你知道你要把数据处理的时候,从connection里面怎么读吗,我们调recv就可以读,我们曾经添加这个fd已经设置为非阻塞了,
在这里插入图片描述
而且他是ET的,根据上节课所讲,ET模式下一旦事件就绪我们只能疯狂的把数据全部读上来,可是数据里面有什么你自己知道吗?
对于tcpserver来讲它在读的时候,应不应该关心数据的格式???
同学们说应该,应该关心数据从哪到哪是个报文,从哪到哪是个什么东西。

答案是 不应该
你是个啥?你是个服务器,
在这里插入图片描述
你只需要关心IO数据就可以,至于这个数据有没有读完,报文的格式细节,这些你不用管。
你只需要帮我把数据全部收上来就可以 了
这才是比较关键的。

我们要处理一个业务,一定是先把数据收上来,收上来之后交给上层去处理,你别越俎代庖的把别人的事情做了,软件分层很重要。

所以接下来Recver只需要读就可以了
connection是形参传进来的,里面不是有fd吗。
我把fd拿进来
那读到哪里呢?
因为是ET模式必须得一直读,循环读,必须把本轮数据全读完,读取过程中根本不用关心读到的数据的格式
所以定义一个基本大小 g_buffer_size = 128
设置char buffer[ g_buffer_size ] 缓冲区
我们用recv 把数据读取到这个buffer中,而且标志位设置为0,代表阻塞读取,可是我们早把fd 设置为非阻塞了,所以这个recv 读取的时候还是非阻塞读取
在这里插入图片描述

recv 返回值
大于 0 读取成功
读取成功该怎么办?你知不知道对应的报文读完了没?你不知道,所以你要一直读,读到他出错。
我们在connection中设置 函数 向connection的string inbuffer直接拼接新内容就可以
在这里插入图片描述
在这里插入图片描述

等于 0 对端把链接关闭了
那我服务器就直接就不用处理这个链接了,
一旦读出现错误,我就直接让连接进入到异常处理回调
在这里插入图片描述

小于 0 读取出错
出错情况一 真的出错
在这里插入图片描述

真的出错,也进入到异常处理,所以所有的读和写所有的异常,全部在Excepter处理就可以了在这里插入图片描述
这也就是为什么当前多路转接这里检测它如果有错误事件继续,异常事件继续,把他的事件全部设成读写事件继续,因为我们想把未来整个服务器所有的异常处理全部都放在这一个Excepter处理

出错情况二
非阻塞一直循环读取的时候,如果出错了,但是errno 错误码 如果等于 EWOULDBOLCK,说明这次读,一直读 读出错了,表明把本轮数据全读完了,读完了我们就不处理了,所以直接break就可以了

另外读取期间,如果 errno 错误码 等于 EINTR 表示读的时候被异常信号中断了,所以continue让他继续去读取。

其他情况才是真正的出错了,也就是第一种情况,这里用else完事。
在这里插入图片描述
所以可以看到connection里的接受缓冲区数据越来越多了。
在这里插入图片描述
读到了数据,可是读完了之后对我来讲,我怎么知道数据 要不要让上层去处理,你不是已经把一批数据全读上来了,此时我怎么知道这个数据在上层应该怎么处理呢?

处理应该交给上层做,你怎么交给上层呢?

服务器已经处理完了IO问题,我们给TcpServer再设置一个 让上层处理信息的回调OnMessage
在这里插入图片描述

把本轮数据全读完,到底这个数据能不能处理 读完之后交给上层,交给上层的时候要把connection对象交给这个回调,因为你读到的sock的所有的数据在connection结构体内部接受缓冲区里。
在这里插入图片描述

直接读完之后回调给上层
在代码中初始化的时候,构造tcpserver时,就需要给我再传个回调
在这里插入图片描述
对于tcpserver来讲OnMessage他是个回调,所有上层就要帮我们处理了,处理的时候一旦读成功了把数据本轮读完了,OnMessage就应该帮我处理,所以OnMessage上层要做的核心工作是:数据有了,但是不一定全,所以要求上层,1.检测 2.如果有完整报文,就处理

所以上层你怎么知道报文是一个完整报文呢?你怎么知道报文完整就要处理,怎么处理呢?
再往上写,就到了之前对特定缓冲区我们根据协议先把报文分包,如果能分出来就分,分不出来就不管,分出来了再反序列化再处理。

那这个回调是谁呢?
这里用输出这个connection缓冲区的数据打印出来测试
在这里插入图片描述
在这里插入图片描述

在往上就到了把报文分割,在做反序列化,就要有协议了,要有应用层了。

我们把之前网络版本计算器的协议相关头文件拿过来

TcpServer.hpp 服务器处理IO 的
Calculator .hpp 处理业务的
两个之间如何耦合呢?
在这里插入图片描述
Handler会将读上来的报文解码反序列化然后计算再序列化编码最后返回响应字符串结果
,接下来要把字符串结果发送出去。
得到计算结果之后要发送出去就和Calculator没关系了,就和服务器有关系,所以一会再绕进入服务器内部再进行处理。

我们的业务逻辑比较简单,没有特别耗时的操作
如果有耗时的操作,我们可以把读到的报文,解码,反序列化,处理计算工作就不做了,可以把之前的线程池拿进来,这里Handler的时候把所有的任务推送 到线程池里,让线程池处理,处理完之后在由线程池把处理的结果写给tcpserver让它再来发。

如果Handler处理结果字符串是空的,那我们就直接返回,让底层继续读,再事件就绪他会继续向inbuffer里追加,
最后一旦有完整的请求了,此时响应就处理完了,它也把缓冲区的字符串该移走就移走了。

下面就要开始发送了
该怎么发送呢?
你想发,前提条件是你要把对应的数据添加到发送缓冲区。
现在又提拱了AppendOutbuffer函数
在这里插入图片描述
我们要发怎么做呢?
我们在连接管理器里只设置了对fd的读关心,我们并没有关心它的写事件,那怎么处理写呢?

在这里插入图片描述

把计算的响应结果字符串添加到connection发送缓冲区里。
在这里插入图片描述

正确的理解发送

1、我们要把数据真正发出去,在epoll/select/poll中,因为写事件表明的本质是发送缓冲区是否有空间,而这个经常是有空间的,所以对于写事件而言,所以经常就是就绪的,
2、如果我们设置对EPOLLOUT事件关心,而EPOLLOUT几乎每次都会就绪,它会导致epoll经常返回,可我们真正关心的是有没有数据发,所以他会浪费CPU 的资源

结论
在写多路转接的代码时
对于读,设置常关心。
对于写,我们是按需设置。

什么是按需设置?
代码体现

3、那怎么处理写呢?
不用考虑epoll,直接写入,如果写入完成,就结束。
如果写入完成,但是数据没有写完,outbuffer里还有内容,我们就需要设置对写事件进行关心了,如果写完了,去掉对写事件的关心!

我们现在上层已经把数据放到outbuffer里,怎么发呢?
在这里插入图片描述

我们直接调用connection里面的Sender回调

首先走到Sender 的时候一定是上层已经在connect里面的outbuffer里面写了数据,当然你不写也行,我会做检测。

因为是ET模式的,非阻塞的,所以接下来while循环

我们直接send发就行了
在这里插入图片描述

send返回值是实际写入的个数
你上层在发的时候,TCP发送缓冲区只剩512字节,可你outbuffer里面可能是1024个字节,所以你期望全发,可实际只能发返回值个。
也就是返回值 > 0
如果返回值大于0,说明他发送成功了,他到底发了多少数据我们也不清楚,接下来发送成功我们要做的就是 从outbuffer里把发送成功的字符移除掉,移除掉返回值个,也就是n个
移除之后要判断,如果outbuffer已经是空了,下次就不发了直接break

返回值 == 0
outbuffer可能因为一些原因没数据,他没发,我们就返回就可以了不处理。

返回值 < 0

发送出错了
意味着当前我们要判断了,因为是一直在发,如果errno == EWOULDBLCOK
说明我一直发,可是最后底层发送缓冲区空间不够了,我上层可能还有数据,但底层不够了,不够了我就不能再发了,所以我就break。

另外errno == EINTR被信号中断了,我就continue

除此之外,发送时候就发出错了,那就打印调试信息,进入异常处理然后return不能让它往后走了

在这里插入图片描述
至此在上层就直接进行发送就行了。
在这里插入图片描述

现在while循环一直发,出来的时候有可能因为发的时候数据没发完,但是已经不能再发了,缓冲区满了。
也有可能outbuffer最后已经整体发完了。
走到异常处理我们不管了
所以接下来该怎么办呢?
发完有两种情况,要么把数据情况了,要么数据还没发完。
没法玩要做判断。
如果outbuffer数据没发完,我已经经过了不断的循环 给他发了很多,可是数据没有发完,此时开启对写事件的关心。
对写事件的关心,一旦底层的写缓冲区有空间了他会就绪,就绪他会继续回调Sender继续发(这里的回调是通过这里来回调的,
在这里插入图片描述

发完之后再去检测。

如果outbuffer为空,说明此次发完了,此时关闭对写事件的关心。

那该怎么开启对写事件的关心呢?

在这里插入图片描述
直接把fd 及其 关心的写事件 设置进epoll模型,利用EPOLL_CTL_MOD

这里把读也设置为true了,也就是也对读设置了关心,为什么?
一定要把EPOLLIN带上,要不它可能认为你不再关心EPOLLIN了,虽然确实是以追加的方式加的,但是这样设置的话确定性会高一些。
其实也证明EPOLLIN是常设的。

一个文件描述符,对应一对接受发送缓冲区,读写都是往一个fd读写。

最终上层就可以通过EnableEvent来开启对读写的关心

上层读取数据后获取响应字符串,之后直接调用Sender,这次发有没有可能没发完呢?
在这里插入图片描述

完全有可能,如果没发完,我们就在Sender开启对写事件关心,
在这里插入图片描述
一旦缓冲区有空间了,底层自动会帮我们做事件派发
在这里插入图片描述
其中就会响应到Sender,而Sender会继续再发,如果继续发还是没发完,继续打开对写事件关心,继续去写,如果此时他发完了,那么他会把写事件关心关闭。

错误更正
这里connection里面只能调用_send_cb
在这里插入图片描述

而不是上文中的Sender
可是发送只能由TcpServer来发,你直接拿connection的_send_cb发,这个方法不应该是你调的。

所以我们可以换种写法,你的connection里面包含了一个TcpServer的回指指针,回指指针可以调用类内的Sender方法。
所以直接调的其实是TcpServer内部的方法。
在这里插入图片描述
用我们之前网络版本计算器客户端来发10次请求测试
在这里插入图片描述
说明服务器是给了响应的,所以我们已经走了一圈了,可是我们最终还要处理异常处理问题。
因为右边是连接退出的时候打印的异常

现在我们到异常处理了
在这里插入图片描述

所有问题都转化成了读写的就绪,读写就绪我们也处理了连接的安全,安全的才进行后续的回调

在这里插入图片描述

现在到了异常这里呢,我认为他一定是出问题了,到底是读出问题了还是写出问题了还是其他的已经不重要了,为什么不重要了呢?
因为我要关掉连接了。

所以接下来该怎么做呢?
首先

  1. 你要在epoll中移除对事件的关心。
  2. 关闭异常的文件描述符
  3. 从unordered_map中移除

在这里插入图片描述

那还要不要把new出来的connection对象再给它 delete呢?
在这里插入图片描述

不用了,因为智能指针自动释放空间。

在这里插入图片描述
比如读出问题了,一旦进入异常处理 就不能再让他往后走了,一旦异常处理以后直接return

double free问题

解决

1.把我们的reactor进行拆分

我们可以把listen模块单独拆出来,然后把
listener关心的事件,就当成一个正常的连接添加进去就可以了。

在这里插入图片描述
有什么好处?
意味着往后如果想把服务设置成多线程
有一种最简单的方式
我们创建一个主线程,主线程内部包含一个TcpServer 这其实就是一个Reactor

未来这是你创建的base_server 主server,它只负责进行获取新链接。
未来把他启动之后呢,后面我们再创建多线程,创建多线程时他要做的事情是Loop

然后线程要执行的函数Loop
每一个线程内部继续创建服务器server,至此就相当于每一个线程都会有一个reactor
在这里插入图片描述
在这里插入图片描述

然后呢由主reactor 它往后获得的所有的连接不用再代码当中让他AddConnection
在这里插入图片描述
那么你做什么工作呢,你在tcpserver里维护一个vector fds
在这里插入图片描述

意思就是说,我在主reactor里我自己想办法获取新链接时,想办法维护一个数组
这个主reactor线程只负责把所有连接放上来,获取到的新fd放到vector里就可以了,主线程可以和其他线程通信,还记得之前写的阻塞队列吗,所以我们可以把vector推送给负载均衡式的交给其他reactor
其他reactor做什么呢?他不绑定listen套接字,其他reactor只需要把所有的文件描述符拿进来在他里面做AddConnection这个工作
在这里插入图片描述

也就是说每一个reactor呢,他都把fd获取上来
然后添加到自己对应的reactor里,所以对于fd生命周期的管理由上面的线程管理,而你的主reactor他只负责获取添加到vector里然后让所有线程读到就可以。

那其他线程怎么读到呢?
在这里插入图片描述

你可以设置阻塞队列,得到了多个连接把vector里 的文件描述符push往里面写,最后通知消费线程来消费,然后其他线程读到之后由其他线程处理

这种方案叫做 == 一个线程一个reactor==
一般叫做 one thread one loop

记住一定要把listen套接字拆出来
listener获取连接的时候,会回指tcp_server。
我们不要调用什么AddConection
他这里获取连接时只需要让它把新的fd push到对应的vector当中
在这里插入图片描述
在这里插入图片描述
push到对应的vector当中呢,再来进行操作

今天TcpServer不应该这么叫,他应该叫做事件循环 EventLoop
它最核心的工作就是它
在这里插入图片描述

2.链接管理

3.Reactor的理论

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

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

相关文章

【深度学习】 零基础介绍卷积神经网络(CNN)

CNN学习 零基础介绍写个CNN最简单的代码一. 概述二. 搭建CNN1. 输入层2. 卷积层3. 激活层4. 池化层5. 全连接层6. 网络搭建小结7. 损失函数8. 梯度下降9. 反向传播10. 模型评估与正则化11. 尝试搭建自己的第一个CNN 三. 经典CNN结构四. 猫狗识别项目实践1. Paddle实现版本&…

Leetcode打卡:找到稳定山的下标

执行结果&#xff1a;通过 题目&#xff1a; 3258 找到稳定山的下标 有 n 座山排成一列&#xff0c;每座山都有一个高度。给你一个整数数组 height &#xff0c;其中 height[i] 表示第 i 座山的高度&#xff0c;再给你一个整数 threshold 。 对于下标不为 0 的一座山&#xf…

leetcode刷题日记03——javascript

题目3&#xff1a; 回文数https://leetcode.cn/problems/palindrome-number/ 给你一个整数 x &#xff0c;如果 x 是一个回文整数&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 回文数是指正序&#xff08;从左向右&#xff09;和倒序&#xff08;从右向…

服务器数据恢复—RAIDZ离线硬盘数超过热备盘数导致阵列崩溃的数据恢复案例

服务器存储数据恢复环境&#xff1a; ZFS Storage 7320存储阵列中有32块硬盘。32块硬盘分为4组&#xff0c;每组8块硬盘&#xff0c;共组建了3组RAIDZ&#xff0c;每组raid都配置了热备盘。 服务器存储故障&#xff1a; 服务器存储运行过程中突然崩溃&#xff0c;排除人为误操…

Tact智能合约安全实践:TON生态系统中的常见错误

TON&#xff08;The Open Network&#xff09;以其创新特性和强大的智能合约性能&#xff0c;不断拓宽区块链技术的边界。基于早期的区块链平台&#xff08;如以太坊等&#xff09;的经验与教训&#xff0c;TON为开发者提供了一个更加高效且灵活的开发环境。其中推动这一进步的…

C进阶—指针(1)

若是阁下满意的话&#xff0c;可否一键三连呢&#xff01; 第一篇进阶指针就是先了解各种新的概念&#xff08;用法我们后面几篇再详细说&#xff01;先只介绍概念&#xff09;&#xff0c;有疑惑很正常&#xff0c;只是暂时的&#xff0c;我们一起来看看吧&#xff01; 字符指…

【Python使用】嘿马头条项目从到完整开发教程第9篇:缓存,1 缓存穿透【附代码文档】

本教程的知识点为:简介 1. 内容 2. 目标 产品效果 ToutiaoWeb虚拟机使用说明 数据库 理解ORM 作用 思考&#xff1a; 使用ORM的方式选择 数据库 SQLAlchemy操作 1 新增 2 查询 all() 数据库 分布式ID 1 方案选择 2 头条 使用雪花算法 &#xff08;代码 toutiao-backend/common/…

谷歌浏览器的扩展程序自动更新设置

谷歌浏览器是全球最受欢迎的网络浏览器之一&#xff0c;其扩展程序更是为用户提供了丰富的功能。然而&#xff0c;随着时间的推移&#xff0c;扩展程序需要更新以修复漏洞、提升性能或增加新功能。本文将详细介绍如何在Chrome中设置扩展程序的自动更新。&#xff08;本文由http…

LabVIEW与PLC点位控制及OPC通讯

在工业自动化中&#xff0c;PLC通过标准协议&#xff08;如Modbus、Ethernet/IP等&#xff09;与OPC Server进行数据交换&#xff0c;LabVIEW作为上位机通过OPC客户端读取PLC的数据并进行监控、控制与处理。通过这种方式&#xff0c;LabVIEW能够实现与PLC的实时通信&#xff0c…

在Windows Server路由和远程访问服务中启用L2TP/IPsec VPN

背景 路由和远程访问服务&#xff08;Routing and Remote Access Services&#xff0c;RRAS&#xff09;是Windows Server上的一个角色&#xff0c;包含很多功能&#xff0c;可以用来搭建VPN。然而&#xff0c;在什么也不做的初始配置中&#xff0c;它只允许PPTP协议连接。然而…

Android简洁缩放Matrix实现图像马赛克,Kotlin

Android简洁缩放Matrix实现图像马赛克&#xff0c;Kotlin 原理&#xff0c;通过Matrix把一个原图缩小到原先的1/n&#xff0c;然后再把缩小后的小图放大n倍&#xff0c;自然就是马赛克效果&#xff08;相当于是放大后像素“糊”成一片了&#xff09;。 import android.content.…

《Posterior Collapse and Latent Variable Non-identifiability》

看起来像一篇很有用的paper&#xff0c;而且还是23年的 没看完 后边看不懂了 Abstract 现有的解释通常将后验崩塌归因于由于变分近似而使用神经网络或优化问题。 而本文认为后验崩塌是潜在变量不可识别性的问题(a problem of latent variable non-identifiability) 本文证明了…

网络视频监控平台/安防监控/视频综合管理Liveweb视频汇聚平台解决方案

一、当前现状分析 当前视频资源面临以下问题&#xff1a; 1&#xff09;不同单位在视频平台建设中以所属领域为单位&#xff0c;设备品牌众多&#xff0c;存在的标准不一&#xff0c;各系统之间也没有统一标准&#xff1b; 2&#xff09;各单位视频平台建设分散、统筹性差&am…

【前端爬虫】关于如何获取自己的请求头信息(user-agent和cookie)

注意&#xff1a;由于user-agent和cookie中保存了部分账户信息&#xff0c;所以一定不要随意泄露给他人&#xff01;&#xff01;&#xff01; 1.首先打开某个页面&#xff0c;点击键盘的F12键进入控制台&#xff0c;或者鼠标右键页面选择打开控制台 2.然后点击控制台上方的网…

共创共建!葡萄城 SpreadJS 完成 HarmonyOS NEXT 操作系统兼容认证

最新技术资源&#xff08;建议收藏&#xff09; https://www.grapecity.com.cn/resources/ 近日&#xff0c;华为“企业工作必备应用鸿蒙化论坛”在北京圆满落幕&#xff0c;论坛汇聚了众多行业精英和合作伙伴&#xff0c;聚焦讨论企业数字化转型与原生鸿蒙生态融合等话题。葡萄…

单项链表的学习

1:链表概念 链表是⼀种物理存储结构上⾮连续、⾮顺序的存储结构&#xff0c;数据元素的逻辑顺序是通过链表中的指针链接次序实现的。 1&#xff1a;结点 与顺序表不同的是&#xff0c;链表⾥的每节"⻋厢"都是独⽴申请下来的空间&#xff0c;我们称之为“结点 / 结…

基于大语言模型的多代理下一代制造系统能灵活动态管理制造资源的高效调度方法

摘要 论文地址&#xff1a;https://arxiv.org/pdf/2405.16887 随着生产率的提高&#xff0c;客户对多品种、小批量生产的需求也在不断增加&#xff0c;这反过来又对制造系统提出了更高的要求。由于这种需求&#xff0c;当生产任务频繁变化时&#xff0c;传统的制造系统往往无法…

FPGA-PS端编程1:

目标 在小梅哥的zynq 7015上&#xff0c;完成以下目标&#xff1a; 读取 S1 按键的电平&#xff0c; 当 S1 按键为按下状态时&#xff0c;驱动 PS LED 以 1S 的频率闪烁(注意理解 1S 的频率闪烁和 1S的时间翻转两种描述之间的差别)&#xff0c; 当 S1 释放后&#xff0c;停止…

模型 QFD(质量功能展开/质量屋)

系列文章 分享 模型&#xff0c;了解更多&#x1f449; 模型_思维模型目录。将客户需求转化为产品设计。 1 模型 QFD&#xff08;质量功能展开&#xff09;的应用 1.1 电信服务及网络维护过程质量改进QFD应用案例 背景介绍&#xff1a; 随着中国加入WTO和国家对电信管制的普遍…

安装@wangeditor/editor-for-vue失败原因

链接: 安装wangeditor/editor-for-vue失败原因 或者下述命令行: 安装成功可到packa.json里面查看&#xff1a;