muduo 28 项目总结

目录

Channel 

Poller、EpollPoller     - Demultiplex多路复用事件分发器

 Channel和Poller之间的关系:

EventLoop - Reactor 

Channel与Poller之间的通信:

 Channe、Poller、EventLoop逻辑:

Thread、EventLoopThread和EventLoopThreadPool

 EventLoopThreadPool​编辑

 EventLoopThread

Acceptor:

Buffer:

 TcpConnection:

 TcpServer:

流程总结:

1.首先看看我们如何使用muduo:

2.一步步来看:

1. TcpServer server_:

 2. TcpServer server_.start()

 3. loop.loop()

总结一下调用的三个步骤:

 4.有了一个新的连接之后怎么做,TcpServer::newConnection后续(连接的建立)

5. 连接成功,有数据通信怎么办?TcpServer::newConnection后续(数据的收发)

6. 连接的关闭


Channel 

fd、events、revents、callback、

两种channel、listenfd-acceptorChannel 、connfd-connectionChannel        

 Channel主要做的事情就是封装了fd_, events_跟revents_,还有一组回调函数。

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

Poller、EpollPoller     - Demultiplex多路复用事件分发器

 std::unordered_map<int, Channel*>

 Channel和Poller之间的关系:

        对于上层来说,如果有一个fd发生,它就会把这个fd打包成Channel通道,然后下发到poller上去,poller中有一个channels_容器,key是这个sockfd,值就是这个打包的channel:

        也就是说如果poller检测到有哪个fd有事件发生了,它就可以通过发生事件的fd,再通过channels_这个map容器找到对应的channel,这个channel里面就记录了详细的事件回调:

        

总共有两种Channel,因为有两种fd:

        一种是listenfd(依赖于acceptor),封装成了acceptorChannel;

        一种是connfd,建立连接时用的,封装成connectionChannel。

        listenfd发生的事件,其所调用的回调函数就是由acceptor设置的;已建立连接的客户端专门通信用的connfd,回调函数由TcpConnection设置。

①:Acceptor设置的回调函数:

②:TcpConnection设置的回调函数:

 

EventLoop - Reactor 

①:using ChannelList = std::vector<Channel*>

ChannelList activeChannels_    //eventloop管理的所有channel

②:unique_ptr<Poller> poller_        //eventloop所管理的poller

③:int wakeupFd_          //loop通过该eventfd进行从mainloop到subloop的通知

        主要作用:一个wakeupfd_隶属于一个loop,每一个loop都有一个wakeupfd_,因为loop最后执行的时候驱动底层的事件分发器,时epoll_wait,只要没有事件发生,相应的loop线程阻塞在epoll_wait上,如果我们想唤醒loop所在线程的阻塞状态,我们通过loop对象获取其wakeupfd_,直接在wakeupfd_上写入东西,相应的loop就会被唤醒。因为每一个wakeupfd_也被封装成wakeupChannel_,是loop的第一个成员变量,同时注册在自己loop底层的epoll事件分发器上。

④:unique_ptr<Channel> wakeupChannel_   //每一个线程都有一个wakeupFd_封装成channel

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

Channel与Poller之间的通信:

        但是Channel和Poller是相互独立的,它们不能直接相互通信,依赖EventLoop进行通信,所以Channel和Poller都有一个成员变量记录当前所在的EventLoop事件循环。 

 Channe、Poller、EventLoop逻辑:

        Poller检测socket有相应事件发生,然后通过EventLoop调用Channel相应的,fd发生事件的回调函数。


Thread、EventLoopThread和EventLoopThreadPool

EventLoop* getNextLoop() : 通过轮询算法获取下一个subLoop,默认指向baseLoop

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

 EventLoopThreadPool

 包括基本的mainLoop、线程的数量、存放所有EventLoop线程、以及所有EventLoop指针。

 EventLoopThread

 底层有当前loop和底层线程;

有一个EventLoopThread::startLoop()函数:

在EventLoopThread的构造函数中我们对创建的线程进行绑定了一个线程执行函数:

        EventLoopThread::startLoop()和EventLoopThread::threadFunc()之间存在一个条件变量通信的问题:startLoop中创建完线程后阻塞,要等待threadFunc中创建好线程对应的Eventloop之后通知再继续运行,one loop per thread

 loop相应初始化完成后,loop.loop()开启当前loop的事件循环-epoll_wait()。

Acceptor:

        Acceptor封装了listenfd相关的操作,比如说:创建listenfd,绑定bind,listen,listen成功后,把listenfd打包,成acceptorChannel扔给baseloop来监听它的事件。

Buffer:

        Buffer是缓冲区,对于nonblocking(非阻塞)的I/0,我们都需要设置缓冲区,涉及到应用写数据写到缓冲区中,再写到TCP的发送缓冲区,然后send数据。

        因为TCP的发送缓冲区,写满了就要发送,这是同步的过程,效率比较慢。

 TcpConnection:

        一个连接成功的客户端对应一个TcpConnection

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

 TcpServer:

总领所有,

①:首先有一个Acceptor

②:EventLoopThreadPool线程池

通过Acceptor获得新用户,然后把新用户封装成Tcpconnection,设置各种回调函数,然后才可从EventLoopThreadPool中getnextLoop,选择一个subloop,把TcpConnection丢给相应subLoop

③:一堆callback回调函数;

④:ConnectionMap:保存所有连接


流程总结:

1.首先看看我们如何使用muduo:

①:首先定义一个EventLoop对象,这个是mainloop

②:创建一个地址,ip:127.0.0.1 、 port:8000

③:调用EchoServer构造函数,

         1. 创建TcpServer; 2.保留loop; 3.设置两个回调函数;4.设置线程数(这个数量不包括baseloop)。很好的把网络代码和业务代码分离,我们只需要关注两个回调函数做什内容就好。

④:调用start();

⑤:启动主线程loop。

2.一步步来看:

1. TcpServer server_:

①:首先是Acceptor的创建:

  1. 设置非阻塞Sockt,listenfd;
  2. 打包成Channel;准备向mainloop的poller上传;
  3. 设置一系列的tcp选项,地址、端口重用,绑定地址;
  4. 给listenfd打包的acceptchannel设置回调函数,accept channel只关心什么?只关心读事件,也就是说呢,我们在这个网络库上只关心了accept channel有新用户连接的时候。

②:EventLoopThreadPool的创建 :

baseloop传进去,创建一个线程池,但是此时还有没有开启loop线程。

③: 有新用户的连接实际上是执行TcpServer::newConnection

 2. TcpServer server_.start()

①:启动线程池

内部调用EventLoopThreadPool::start:

        EventLoopThread::threadFunc函数创建loop并启动loop,epoll_wait开始阻塞监听。每一个loop中都有一个weakupfd_,保证唤醒对应睡眠中的subloop。

②:启用Acceptor的listen方法

相当于把acceptorchannel注册在mainloop的poller上。(把acceptorChannel注册在baseLoop上)

 3. loop.loop()

开启baseloop的loop().

总结一下调用的三个步骤:

1、构建TcpServer对象,同时包含了注册回调,设置底层线程的个数,和扩展。
2、start开启loop子线程,注册wakeupfd,能够让主线程mainloop来唤醒子线程loop。然后acceptorlisten,把listenfd打包成一个acceptorChannel注册到baseloop上。
3、最后启动loop();

 4.有了一个新的连接之后怎么做,TcpServer::newConnection后续(连接的建立)

主要就是:

  • 从EventLoopThreadPool轮询拿到一个subloop;
  • 然后构建一个TcpConnection对象,并设置回调函数到底层的Channel中去;
  • 重要的是设置了一个removeCallback关闭连接的回调函数。
  • 最后执行了一个ioLoop->runInLoop(std::bind(&TcpConnection::connectEstablished, conn));

  • 进入 connectEstablished:

 ①: 

        一个connection肯定是包含有一个Channel,因为一个Connection表示一个connectfd,就会打包成1个Channel。

        用Channel绑定了当前TcpConnection(tie方法):

        因为只有Channel才能收到Poller给它通知的事件回调,它执行的事件回调都是TcpConnection设置给它的,如果TcpConnection由于一些原因这个对象TcpConnection没有了,那Channel到时候就不会执行回调了。
        所以,Channel在这里使用了weakptr弱智能指针来记录了这个TcpConnection对象,到时候通过弱智能指针的提升来监测TcpConnection是否存活,存在就执行相应的回调,不存在就不执行了。

②:

        然后Channel调用enableReading函数,向相应的Poller注册Channel,唤醒一下subloop,把当前TcpConnectionchannel注册在它选择的某个subloop上;

③:

        执行connectionCallback_,由Tcpsever构造时传入。

5. 连接成功,有数据通信怎么办?TcpServer::newConnection后续(数据的收发)

        对于新连接来说,都是enableReading,注册了socketfd的epollin事件,如果有相应的可读事件到来,那么相应的loop线程的poller就会返回,返回以后就执行相应的Channel的readcallback_回调函数!

 Channel中的readcallback_回调函数,由TcpServer给TcpConnection设置!

去到 TcpConnection::handleRead中:

 我们在测试代码中将messageCallback设置为

6. 连接的关闭

         如果有异常,对端关闭了或者是当前服务端业务执行完之后在这里主动调用shutdown,底层的Channel都会响应closecallback回调函数!

 TcpConnection给Channel传入回调函数,实际上是TcpConnection从TcpServer处拿到的回调函数,再封装成handleClose,传给Channel!

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值