重写muduo网络库之调用流程的梳理

news2024/11/19 3:25:46

目录

1、流程叙述

2、我们看看TcpServer的构造都做了什么?

3、start()

3.1 开启loop

3.2 连接的建立

3.3 数据的收发

4、连接的关闭


muduo网络库各组件梳理见此博客

重写muduo库之组件梳理

1、流程叙述

首先,我们是怎么使用的?

1.定义一个loop,就是baseloop
2.InetAddress打包了IP地址和端口号
3.创建server对象,给到EchoServer的构造函数,通过其参数进行初始化底层的TCPserver对象(相当于创建了TcpServer对象),保留了loop
4.设置了2个回调setConnectionCallback、setMessageCallback
5.设置了底层的loop线程的数量(这个数量不包括baseloop)

很好的把网络代码和业务代码分离了,我们只需要做开发者需要关注的:
6.我们用户作为开发者只需要关注下面的2个方法:

响应连接的建立和断开、响应读写事件

设置完之后
7.server.start();启动loop   loop.loop()

总结而来就是:首先构建tcpserver对象,设置回调方法,然后设置底层的loop线程的数量,然后调用start方法,最后开启主线程的loop

2、我们看看TcpServer的构造都做了什么?

创建了Acceptor,EventLoopThreadPool(还没有开启loop线程)

首先Acceptor创建了一个非阻塞的listenfd,然后把它打包成1个acceptchannel,准备往mainloop的poller上扔。

然后设置了一些tcp选项,创建socket,绑定bind,给channel设置回调,就是readcallback

也就是说:Acceptchannel只关心读事件。
只关心acceptchannel有新用户的连接的事件,底层的channel会去执行readcallback,对于Acceptor的readcallback执行的是handleRead

我们回过来继续看tcpserver的构造函数,接下来创建了EventLoopThreadPool(还没有开启loop线程),

然后执行了setNewConnectionCallback,我们刚才看到acceptor的构造函数

绑定了一个setReadCallback,也就是说,有新用户的连接,底层的channel就会去执行readcallback,因为有新用户的连接,响应了epollin事件,然后就执行readcallback

对于acceptor的setReadCallback执行的就算是Acceptor::handleRead。

我们进去看看handleread,会发现执行的是newConnectionCallback这个回调

这个里面的回调newConnectionCallback是谁设置的?通过setNewconnectionCallback设置的


这个setNewconnectionCallback是谁设置的?是最上层TcpServer设置的!

由上图,我们也就知道了,注册在mainloop上的acceptor,当有新用户连接,成功以后,经过上面一串,最终执行的是TcpServer的newConnection方法。

我们继续看TcpServer

3、start()

3.1 开启loop

通过atomic变量started控制一下,注意要将其初始化为0,只能取启动1次。

我们看第一句代码,threadpool启动底层线程池,创建loop子线程,并且开启loop的loop()
子线程创建执行的就是

我们设置的线程的回调方法就是

58行马上就启动loop()了
启动的时候,为了能把处于睡眠中的subloop唤醒,每个loop都有一个wakeupfd注册在相应loop的子线程的poller上。

接着我们看start的第二句

执行acceptor的listen方法,把acceptorchannel注册在baseloop上的poller上。

这一切就都准备好了
main函数最后一句开启baseloop的loop

我们在调用的三步骤:
1、构建TcpServer对象,同时包含了注册回调,设置底层线程的个数,和图中扩展的内容。
2、start开启loop子线程,注册wakeupfd,能够让主线程mainloop来唤醒子线程loop。然后acceptor listen,把listenfd打包成一个acceptorchannel注册到baseloop上。
3、最后启动loop();

我们看看acceptor的listen

muduo库:mainloop和subloop之间并没有同步队列,并没有使用生产者消费者模型:mainloop生成连接,放到同步队列里面,subloop从同步队列去自己取连接,做成异步的生产连接和消费连接。并没有。
而是用系统调用eventfd,创建wakeupfd,可以用它直接做线程间的notify,就是通知,唤醒,效率是非常非常高的。
在libevent上用的是socketpair这个系统调用,创建的是基于Unix本地通信的双向通信的套接字。

我们继续看,现在有一个新的连接了,mainloop就要返回了,执行acceptorchannel的readcallback,最终调用的是TCPserver的newconnection,就是有一条新的连接来了。

首先,通过轮询算法选择一个subloop,让这个ioloop指针指向它

接着创建1个tcpconnection对象

然后设置了一些回调,这些回调将来由TcpConnection设置到底层的channel里面。


主要的是设置了CloseCallback,当fd被关闭的时候,最终回调到TcpServer 的removeconnection,最终执行下面这个方法

这个ioLoop是肯定是在当前的主线程里去执行的,但是主线程访问ioLoop访问的肯定是一个子线程对应的loop,所以这里的ioLoop->runInLoop肯定不是在主线程里面执行的,除非是没有设置子线程(setthreadnum),ioLoop永远指向baseLoop。
我们看看runInLoop函数

如果执行的loop就在当前线程中(没有设置子线程数量),直接执行回调cb(),否则queueInloop。

queueInloop就是:每一个eventloop都有一个成员变量:wakeupfd,现在要在一个subloop里面执行下面这个方法:


通过这个指针找到subloop的wakeupfd,往wakeupfd上写数据,把相应的subloop唤醒,唤醒以后就去执行这个connectionEstablished,

3.2 连接的建立

我们进入connectEstablished


设置了状态,一个connection肯定是包含有一个channel,因为一个connection表示一个connectfd,只要有socketfd,就会打包成1个channel。
这里用channel绑定了当前 的TcpConnection,因为只有channel才能收到poller给它通知的事件回调,它执行的事件回调都是TcpConnection设置给它的,如果TcpConnection由于一些原因这个对象TcpConnection没有了,那channel到时候还执行不执行回调?
所以,channel在这里使用了 weakptr弱智能指针来记录了这个TcpConnection对象,到时候通过弱智能指针的提升来监测TcpConnection是否存活,存在就执行相应的回调,不存在就不执行了。避免了错误发生。
然后channel调用enableReading函数,向相应的poller注册channel,唤醒后把当前TcpConnectionchannel注册在它选择的某个subloop上了,然后再执行connectionCallback,对于测试代码来说,我们看到的就是下图代码中,日志的打印显示建立的成功。

在主函数调用中,新连接来了,就是上述的处理过程。

3.3 数据的收发

连接成功,有数据通信怎么办?
对于新连接来说,都是enabler eading注册了socketfd的epollIn事件,如果有相应的可读事件到来,那么相应的loop线程的poller就会返回,返回以后就执行相应的channel的readcallback事件


通过的是

执行的就是Tcpconnection的handleread

handleread开始读数据,数据读上来

调用messagecallback,相应的响应就是给上层应用报上来,onMessage这个函数被调用了,回调给用户。

通过buf->retrieveALLAsString拿到原始字符串,反序列化得到json或者pbbuff,然后业务处理,然后响应send。
所以说,相应的channel有可读事件发生,subloop就会回调channel的readcallback,tcpconnection给相应的channel的readcallback绑定的就是tcpconnection的handleread,handleread通过inputbuffer输入缓冲区读取相应的socketfd上的数据,读取完,回调用户设置的messagecallback,

然后用户这里就响应了

4、连接的关闭

如果有异常,对端关闭了或者是当前服务端在这里主动调用shutdown,底层的channel都会响应closecallback

这个closecallback,TCPconnection设置的是哪个回调?handleClose


handleclose执行disableAll把对应的channel和感兴趣的事件从poller上删除。

然后执行用户的回调。


对于使用者来说,onconnection又被回调了,这次是断开连接的回调。

执行tcpconnection的


执行TcpServer::newConnection的


就调用到removeConnection

执行removeConnectionInLoop


在TCPserver里,有一个connectionMap,把这个connection从connectionsmap删除掉,然后getloop,获取这条连接所在的子loop,执行connectDestroyed,

因为已经在handleclose中设置成kDisconnected了,所以此处进不去if语句,直接执行channel_->remove(),删掉channel,如果从其他地方进来,状态是已连接的,则执行if语句,断开连接

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

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

相关文章

每日一学—K邻算法:在风险传导中的创新应用与实践价值

文章目录 📋 前言🎯 K邻算法的实践意义🎯 创新应用与案例分析🔥 参与方式 📋 前言 在当今工业领域,图思维方式与图数据技术的应用日益广泛,成为图数据探索、挖掘与应用的坚实基础。本文旨在分享…

番外篇 | 手把手教你利用YOLOv8进行热力图可视化 | 针对视频

前言:Hello大家好,我是小哥谈。YOLOv8的热力图可视化可以帮助我们更加直观地了解模型在图像中的检测情况,同时也可以帮助我们进行模型的调试和优化。热力图是一种颜色渐变的图像,不同颜色的区域表示不同程度的关注度或者置信度。在YOLOv8中,可以通过设置阈值来控制热力图的…

关键字详解

1.用于定义访问权限修饰符的关键字 面向对象程序三大特性:封装、继承、多态。 1.1 访问权限符 Java 中主要通过类和访问权限来实现封装: 类可以将数据以及封装数据的方法结合在一起 ,更符合人类对事物的认知,而访问权限用来控制…

怎样计算Excel一列数值中十位数为5的个数?

有一列数字,可能正数也可能是负数,有可能有小数,要怎么计算这列数字中十位数为5的数量有多少个? 一、按示例情况,数字均为整数 公式如下: SUM(--(MID(A1:A6,LEN(A1:A6)-1,1)"5")) 数组公式&a…

python的文件操作及函数式编程介绍

五、文件操作 1、读取键盘输入 input 获取标准输入,数据类型统一为字符串 #!/usr/bin/python # -*- coding: UTF-8 -*- str input("请输入:") print("你输入的内容是: ", str) 这会产生如下的对应着输入的…

解锁客户需求密码:银行业数据分析在业务决策中的关键作用

一、引言 在数字化和大数据时代的浪潮下,银行业正经历着前所未有的变革。作为数据分析领域的资深专家,我深知数据分析在银行业务发展中的重要性和价值。本文将从银行业数据分析的角度出发,深入探讨相关业务场景下的数据分析应用,…

基于spingboot,vue线上辅导班系统

目录 项目介绍 图片展示 运行环境 获取方式 项目介绍 权限划分:用户,管理员 具有前后台展示,前台供用户使用;用户具有自己的后台,查看自己的老师课程等;管理员具有最大的权限后台。 用户&#xff1a…

Linux 第二十九章

🐶博主主页:ᰔᩚ. 一怀明月ꦿ ❤️‍🔥专栏系列:线性代数,C初学者入门训练,题解C,C的使用文章,「初学」C,linux 🔥座右铭:“不要等到什么都没有了…

vuex的基本认知

目录 一、什么是vuex 二、vuex的应用场景 三、vuex的优势 一、什么是vuex Vuex是一个vue的状态管理工具,状态就是数据。 进一步解释:vuex是一个插件,可以帮助我们管理vue通用的数据(多组件共享的数据) 二、vuex的…

2024中国(厦门)国际医用消毒及感控设备展览会

2024中国(厦门)国际医用消毒及感控设备展览会 2024 China (Xiamen) International Medical Disinfection And Infection Control Exhibition 致力于打造医用消毒及感控设备产业采购一站式平台 时 间:2024年11月1-3日 November 1-3, 2024 …

算法_前缀和

DP34 【模板】前缀和 import java.util.Scanner;// 注意类名必须为 Main, 不要有任何 package xxx 信息 public class Main {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别int n in.nextInt(),q in.ne…

Vmware ESXi无法创建虚拟机

点击创建虚拟机,没有反应 esxi在网页端无法创建虚拟机,与浏览器插件supercopy超级复制有关。 关闭插件在此页面运行,即可解决问题。 这个插件严重影响虚拟机正常的操作, 我还以为我的虚拟机炸了,格式化后,又…

【风变】Python爬虫精进复习-20240430

参考笔记 下面给出一个巨佬学习风变pyhton基础语法和爬虫精进的笔记(链接) 风变编程笔记(一)-Python基础语法 风变编程笔记(二)-Python爬虫精进 技术总结 request BeautifulSoup selenium BeautifulSoup 练习0-1:文章下载 import requ…

鸿蒙 DevEcoStudio:用户名密码获取保存

【使用首选项实现用户名密码保存获取】 打开src/main/ets/entryability路径下的EntryAbility.ts文件 在 export default class EntryAbility extends UIAbility {onCreate(want, launchParam) {hilog.info(0x0000, testTag, %{public}s, Ability onCreate);下边添加内容&…

ASP.NET邮件收发程序的设计与开发

摘 要 《邮件收发程序的设计与开发》是一个综合性的程序设计,涉及到界面、系统、数据库、协议、编码等多个方面的内容。本设计前台采用.NET技术,后台数据库采用SQL Server 2000,语言采用C#,主要讲述了邮件系统的注册、登陆、管理、发送和…

【激活函数--下】非线性函数与ReLU函数

文章目录 一、非线性函数在神经网络中的重要性二、ReLU函数介绍及其实现2.1 ReLU函数概述2.2 ReLU函数的Python实现及可视化 一、非线性函数在神经网络中的重要性 在神经网络中,激活函数的选择对于网络的性能和能力至关重要。阶跃函数和Sigmoid函数除了是激活函数的…

ssti学习(1)

一、成因: 渲染模板时,没有严格控制对用户的输入。(使用了危险的模板,导致用户可以和flask程序进行交互) flask是一种基于web开发的web服务器,如果用户可以和flask交互,则可以执行eval、syste…

智慧公厕:公共厕所信息化的创新之路

公共厕所是城市建设中不可或缺的一环,但长期以来,由于管理不善和设施落后,公厕成为城市环境中的一大难题。为了解决这个问题,变革式的智慧公厕应运而生。 智慧公厕是一种借助物联网、互联网、大数据、云计算、自动化控制等技术整…

umi项目配置之项目构建时配置umirc.ts

对于 umi 中能使用的自定义配置,你可以使用项目根目录的 .umirc.ts 文件或者 config/config.ts,值得注意的是这两个文件功能一致,仅仅是存在目录不同,2 选 1 ,.umirc.ts 文件优先级较高 umi 的配置文件是一个正常的 n…

PHP黑魔法之md5绕过

php本身是一种弱语言,这个特性决定了它的两个特点: 输入的参数都是当作字符串处理变量类型不需要声明,大部分时候都是通过函数进行类型转化php中的判断有两种: 松散比较:只需要值相同即可,类型不必相同,不通类型比较会先转化为同类型,比如全数字字符串和数字比较,会比…