重写muduo库之组件梳理

目录

1、概述

2、Channel+Poller

3、EventLoop

4、EventLoopThread

5、Acceptor

6、TcpConnection

7、TcpServer


1、概述

mainReactor就是我们代码中的mainloop(baseloop),subReactor是我们代码中的工作线程进行读写事件的处理,读数据(read)是底层做的,decode是我们自己做的。compute和encode也是我们自己做的。从网络上read数据,然后从网络上send数据,这些是由muduo库帮我们做的。decode compute encode是我们用户在OnMessage处理的业务逻辑。每一个loop对应一个线程,每一个线程都是一个独立的Reactor。

2、Channel+Poller

看一下channel的成员变量:

channel主要做的事情是:
封装了fd,events和revents,还有一组回调方法。

  • fd就是表示要往poller上注册的文件描述符
  • events就是事先设置的fd所感兴趣的事件(读事件或者写事件)
  • revents就是poller最终给我们channel通知的这个fd上发生的事件,channel根据相应的发生的事件来执行相应的回调。

对于上层来说,如果有一个fd的话,它就会把fd打包成channel通道,然后下发到poller上。

看一下poller的成员变量:

poller有一个成员变量channels,是一个map表,键就是channel打包的sockfd,值就是包含fd对应的channel,也就是说如果poller检测到哪个fd有事件发生了,就可以通过发生事件的fd和这个map表,找到这个fd对应的channel,这个channel里面就记录着详细的对应事件的回调方法。

但是channel和poller是独立的,它们是不能直接互相通信的,它们都是依赖Eventloop来通信的。
不管是channel还是poller,都有一个成员变量记录它所在的事件循环(loop成员变量)。

muduo库不管是监听的listenfd还是accept返回,客户端连接成功,返回跟客户端专门通信的connectionfd,都会把这个fd打包成channel,给它写入fd感兴趣的事件(读或者写事件),然后注册到相应的loop的poller上去。
poller,我们在项目中实现的主要就是epollpoller,底层就是epoll,意味着底层就是通过epoll_ctrl把相应的channel里面包含的fd注册到epoll上,当epoll返回以后,因为poller上有map表channels,通过sockfd找到它所对应的channel来调用相应的回调。
channelcallbacks回调是上层设置的。
我们总共就有两种channel,因为只有两种fd:listenfd(依赖于accptor)和已建立连接的客户端专门通信用的connectionfd。

channel在调用相应的回调,比如说是listenfd发生的事件,那么它所调用的回调函数就是acceptor塞给它的,如果是已建立连接的客户端专门通信用的connectionfd,肯定就是TCPconnection塞给它的。channel和poller之间不能互相访问。它们2个都是依赖eventloop事件循环的,eventloop相当于是reactor、Poller和Epollpoller相当于是多路事件分发器。

3、EventLoop

看一下EventLoop的成员变量:

我们看到,它有一个activeChannels_,这是一个vector,包含了所有的channel,因为上层交给反应堆的不只是单纯的fd,因为反应堆获取fd,还要执行相应的回调,所以塞给反应堆的都是channel,我们不要忘记,它还有weakupfd!
wakeupfd也是被封装成wakeupchannel_,主要作用是一个wakeupfd,是隶属于一个loop,也就是说,每个loop都有一个wakeupfd,因为loop最后执行触发底层的事件分发器,是epoll_wait,只要没有事件发生,相应的loop线程阻塞在epoll_wait上,如果我们想唤醒某一个loop所在线程的阻塞状态,那么我们直接通过loop对象获取其wakeupfd,直接在这个wakeupfd上写个东西,相应的loop就会被唤醒。因为每个loop的wakeupfd也被封装成一个wakeupchannel,注册在自己loop底层的epoll事件分发器上。

从大体上看,eventloop管理的就是一堆channel和一个poller,和自己的wakeupfd

这样一来,channel想把自己注册到poller上,或者是在poller上修改自己感兴趣的事件,channel都是通过eventloop获取poller的对象,来向poller设置,同样的,如果poller监测到有socketfd有相应的事件发生,就通过eventloop调用channel相应的fd所发生事件的回调函数

它们三个是紧紧相连的

我们看到,eventloop上有一个vector(pendingFunctors),存了一堆的回调函数。

因为这些回调,每一个回调在执行的时候都应该是在loop自己所在的线程执行,如果说当前线程调用了loop,让这个loop执行回调,在程序逻辑上会进行判断,如果当前线程就是这个loop那么直接执行回调,否则的话,就把它存到pendingFunctors这个vector里面,然后唤醒相应的loop,然后在vector里拿相应的回调执行。

4、EventLoopThread

Thread是底层的线程。
EventloopThread是事件的线程
EventloopThreadPool是事件循环线程池
EventloopThreadPool里面有一个方法:getNextloop方法,默认是通过轮询算法获取下一个subloop,如果客户没有通过setthreadnumber设置线程,那么就是没有创建subloop,getNextloop永远返回的是baseloop
当我们通过setthreadnumber设置底层的线程数量时,EventloopThreadPool就会驱动底层开始创建线程,一个线程一个loop,就是one loop per thread

EventloopThreadPool的成员变量如下:

EventloopThread的成员变量如下:

startLoop创建了一个新的事件循环,包含了底层的线程模型thread

下面这个方法是启动新线程,新线程的执行函数,每一个新线程创建了一个独立的EventLoop,开启当前loop的事件循环:

初始化好之后,开始epoll_wait(loop.loop)

5、Acceptor

Acceptor主要封装了listenfd相关的操作,比如说:创建listenfd,绑定bind,监听listen,listen成功后,把listenfd打包成acceptorchannel扔给baseloop来监听它的事件。
Buffer是缓冲区,对于nonblocking(非阻塞)的I/O,我们都需要设置缓冲区,涉及到应用写数据写到缓冲区,再写到TCP的发送缓冲区,然后send数据

因为TCP的发送缓冲区,写满了就要发送,这是同步的过程,效率比较慢。
如果有缓冲区的话,我们用户写数据,直接向缓冲区进行写就可以了。
缓冲区里有prependable,readeridx,writeridx,这3个下标


prependable有8个字节,是头部信息,刚开始readeridx和writeridx都在这8个字节处的位置,然后我们写数据进去,写好的东西就可以是待发送的东西,由Buffer专门进行在缓冲区读取数据进行发送,我们从缓冲区可以进行send发送,用户也可以不断从缓冲区写数据,通过readeridx和writeridx处理
模仿了Java的netty网络库底层提供的channelbuffer这个缓冲区
应用产生数据快,tcp发送的慢,难道每一次发送数据都要等tcp发送完上一次的数据再发送当前数据?写数据到buffer,然后让系统去调用write,或者系统调用read,把数据读到buffer上。相当于应用发送或者接收数据和tcp真实发送或者接收数据就成异步的了,这就非常高效了!

6、TcpConnection

一个连接成功的客户端对应一个TcpConnection
看一下TcpConnection的成员变量:

封装了socket,channel,和各种回调,高水位线的控制(不要发送过快),发送和接收缓冲区。
对于connectionfd在channel上执行的回调,都是由TcpConnection来设置的

7、TcpServer

TcpServer就是总领所有
TcpServer的成员:

  • Acceptor:得到新用户,才能通过EventLoopThreadpool的getnextloop,把新用户封装成Tcpconnection,设置各种各样的回调以后,才能选择一个subloop,把新的connection扔给相应的loop
  • EventLoop
  • Threadpool
  • 一堆callback
  • connectionmap

  • 10
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值