博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
(P26-P27)muduo_base库源码分析:muduo网络库的类图,定时器函数选择
阅读量:4299 次
发布时间:2019-05-27

本文共 4763 字,大约阅读时间需要 15 分钟。

文章目录

1.muduo网络库的类图

  • 类图如下:

    在这里插入图片描述

  • 解释如下:

    灰色是内部类,对外不可见;
    白色是外部类,对外可见;

  • EventLoop类

    EventLoop是对事件循环的抽象;

  • Poller类,PollPoller类,EPollPoller类

    (1)下面的三个是muduo唯一使用面向对象编程思想的地方,下面三个类都是内部类,Poller是抽象类,有三个纯虚函数,muduo的对外接口不使用虚函数来暴露的:
    Poller是对I/O复用的抽象;
    PollPoller派生类继承至Poller,是对poll的封装;
    EPollPoller派生类继承至Poller,是对epoll的封装;
    (2)EventLoop与Poller关系是组合,一个EventLoop包含一个Poller(实心表示组合关系),且Poller的生存期由EventLoop来控制,EventLoop的poll函数实际上是调用Poller的poll函数来实现;

  • Channel类

    (1)Channel实际是I/O事件的注册响应封装,update()成员函数负责注册更新I/O可读可写事件,handleEvent()对所发生的I/O事件进行处理;
    (2)update()成员函数负责注册更新I/O可读可写事件时,又会调用到EventLoop.updateChannel(),从而调用Poller.updateChannel(),相当于将Channel注册到Poller中(将/O可读可写事件注册到Poller中);
    (3)一个EventLoop包含多个Channel,即EventLoop可以捕捉多个通道的可读可写事件;
    (4)EventLoop与Channel的关系是聚合关系(空心表示聚合,一对多),EventLoop不负责Channel生存期的控制,Channel的生存期由TcpConnection,Acceptor,Connector这些来控制;
    (5)Channel不拥有fd,其关闭的时候,不用close关闭fd;

  • FileDescriptor

    (1)FileDescriptor不是一个类,与Channel是关联关系,一个Channel拥有一个fd,一个EventLoop拥有多个fd,用来监听多个fd的相关事件;
    (2)FileDescriptor是被Socket所拥有,其生存期由Socket来控制,当Socket对象销毁时,需要close关闭fd;

  • TcpConnection,Acceptor,Connector类

    (1)Channel是TcpConnection,Acceptor,Connector的成员,他们之间的关系是组合;
    (2)Channel的生存期由TcpConnection,Acceptor,Connector这些来控制;
    (3)Acceptor是对被动连接的抽象,它关注的是监听socket的可读事件,监听socket的可读事件由Channel来注册,调用Channel.handleEvent(),从而调用了Acceptor.handleRead(),这是基于对象的编程思想;
    (4)Connector是主动连接的抽象,调用方式与上类似;
    (5)一旦被动连接或者主动连接建立,则会得到已连接socket(获得TcpConnection),对已连接socket的抽象就是TcpConnection;

  • TcpServer,TcpClient类

    (1)TcpServer包含一个Acceptor,TcpClient包含一个Connector;
    (2)组合关系:TcpServer控制Acceptor的生存期,TcpClient控制Connector的生存期;
    (3)聚合关系:一个TcpServer包含多个TcpConnection,但是它不控制TcpConnection的生存期;
    TcpClient类似;

  • Channel其他

    (1)Channel是selectable IO channel,负责注册与响应IO 事件,它不拥有file descriptor。
    (2)Channel是Acceptor、Connector、EventLoop、TimerQueue、TcpConnection的成员,生命期由后者控制。因为EventLoop还包含一个特殊的Channel对象,用来唤醒loop()

2.时序图

  • 时序图

    在这里插入图片描述

  • 解释

    (1)EventLoop.loop()实际上是调用Poller.poll(),可能是PollPoller.poll()或者EPollPoller.poll()函数(Poller是一个抽象类),poll()之后会返回一些通道Channel,fillActiveChannels检测一些Channel的fd产生的一些事件则返回ActiveChannels,接着调用Channel.handleEvent(),接着回调user callbacks( TcpConnection,Acceptor,Connector类包含Channel,则会调用TcpConnection,Acceptor,Connector里面相应的成员函数来进行回调)。
    (2)若activeChannels不止一个,则还有一个个Channel,EventLoop.loop()再次调用Poller.poll()

  • eg:26\jmuduo\muduo\net\EventLoop.h

    26\jmuduo\muduo\net\EventLoop.cc
    26\jmuduo\muduo\net\Channel.h
    26\jmuduo\muduo\net\poller\PollPoller.h
    26\jmuduo\muduo\net\poller\PollPoller.cc
    26\jmuduo\muduo\net\CMakeLists.txt

  • loop函数的时序图

    调用Poller.poll()函数,会返回一些活动通道fillActiveChannels,接着遍历ActiveChannels进行处理,调用通道的handleEvent()来处理,回调user callbacks来处理。
    在这里插入图片描述
    poll()之前需要注册一些事件,注册事件由Channel.enableReading()或者Channel.enableWriting()发起的,调用EventLoop.UpdateChannel(),进一步调用Poller.UpdateChannel()
    在这里插入图片描述

3.定时器函数选择

  • 定时函数,用于让程序等待一段时间或安排计划任务:
sleep alarm usleep nanosleep clock_nanosleep getitimer / setitimer timer_create / timer_settime / timer_gettime / timer_delete timerfd_create / timerfd_gettime / timerfd_settime
  • 定时器函数选择
    timerfd_* 入选的原因:
    (1)sleep / alarm / usleep 在实现时有可能用了信号 SIGALRM,在多线程程序中处理信号是个相当麻烦的事情,应当尽量避免(多线程程序中尽量避免使用信号)
    (2)nanosleep 和 clock_nanosleep 是线程安全的,但是在非阻塞网络编程中,绝对不能用让线程挂起的方式来等待一段时间,程序会失去响应。正确的做法是注册一个时间回调函数。
    (3)getitimer 和 timer_create 也是用信号来 deliver 超时,在多线程程序中也会有麻烦。
    (4)timer_create 可以指定信号的接收方是进程还是线程,算是一个进步,不过在信号处理函数(signal handler)能做的事情实在很受限。
    (5)timerfd_create 把时间变成了一个文件描述符,该“文件”在定时器超时的那一刻变得可读,这样就能很方便地融入到 select/poll 框架中,用统一的方式来处理 IO 事件和超时事件,这也正是 Reactor 模式的长处。
    若非要处理信号的话,可以将信号转变为fd来处理,使用man signalfd
#include 
int timerfd_create(int clockid, int flags);(定时器类型,创建选项),返回fdclockid:定时器类型有2种:(1)CLOCK_REALTIME:手动更改系统事件会触发(2)CLOCK_MONOTONIC:手动更改系统时间,是不会触发的。一般使用这种定时器flags:TFD_NONBLOCK:该fd是非阻塞的TFD_CLOEXEC:该fd在exec的时候不会被继承下去int timerfd_settime(int fd, int flags, const struct itimerspec *new_value, struct itimerspec *old_value);设定定时器是多长时间的定时操作,设定超时时间:new_value,原来的超时时间:old_value类似:man setitimerstruct timespec {
time_t tv_sec; // seconds long tv_nsec; // and nanoseconds};struct itimerspec {
struct timespec it_interval;//间隔时间,第二次超时时间就是根据这个值来确定。 //若是一次性的定时器,该值可以设置为0 struct timespec it_value;//第一次超时时间};int timerfd_gettime(int fd, struct itimerspec *curr_value)
  • eg:27\jmuduo\muduo\net\EventLoop.cc

    27\jmuduo\tests\Reactor_test03.cc
    27\jmuduo\muduo\net\poller\DefaultPoller.cc
    27\jmuduo\muduo\net\poller\EPollPoller.h
    27\jmuduo\muduo\net\poller\EPollPoller.cc

  • eg测试:27\jmuduo\tests\Reactor_test03.cc

    27\jmuduo\tests\CMakeLists.txt

  • 测试:等待下一个事件超时了,超时时间是10s(const int kPollTimeMs = 10000;//10s超时),会打印nothing happended

    在这里插入图片描述

  • eg测试:27\jmuduo\tests\Reactor_test03.cc

    27\jmuduo\tests\CMakeLists.txt

void timeout(Timestamp receiveTime){
printf("Timeout!\n"); //uint64_t howmany; //把数据读走,否则会一直触发,因为poll是高电平触发的,缓冲区有数据会一直是高电平的 //::read(timerfd, &howmany, sizeof howmany); g_loop->quit();}
  • 测试:没有把timerfd读走,会一直处于高电平的状态,一直触发。这不是因为定时器间隔性的在触发。
    若要设置间隔性的触发,则需要设置it_interval参数。
    在这里插入图片描述

转载地址:http://xmiws.baihongyu.com/

你可能感兴趣的文章
vnpy学习_06回测结果可视化改进
查看>>
读书笔记_量化交易如何建立自己的算法交易01
查看>>
设计模式03_工厂
查看>>
设计模式04_抽象工厂
查看>>
设计模式05_单例
查看>>
设计模式06_原型
查看>>
设计模式07_建造者
查看>>
设计模式08_适配器
查看>>
设计模式09_代理模式
查看>>
设计模式10_桥接
查看>>
设计模式11_装饰器
查看>>
设计模式12_外观模式
查看>>
设计模式13_享元模式
查看>>
设计模式14_组合结构
查看>>
设计模式15_模板
查看>>
海龟交易法则01_玩风险的交易者
查看>>
CTA策略02_boll
查看>>
vnpy通过jqdatasdk初始化实时数据及历史数据下载
查看>>
设计模式19_状态
查看>>
设计模式20_观察者
查看>>