pronet实现

  • update 1
    在TcpServer::readHandler中需要处理两种消息,一种是来自listenfd的EPOLLIN事件,需要创建新的channel,将TcpServer::readhandler注册为channel的回调函数(传递回调函数可传递正在调用的这个函数),然后调用Channel::enableReading(),将channel加入m_channelsList中,m_channelsList的作用为在~TcpServer中delete channel,以此来解决内存泄露的问题

channel使用时需要enableReading(),该函数的作用为监听EPOLLIN事件,将channel对应的fd注册到epoll中(epoll在TcpServer中注册)

事件循环放在TcpServer::start()中,该函数需要做的工作是在收集活动的channel到m_activeChannels中,将返回的事件注册到channel中,channel的指针从events获得,在channel的update()中有将channel的指针存储到event.data.ptr中这步操作,在epoll响应后该参数会传递到对应的events的元素中,因此可以从events中获得channel指针。收集完活动tongguo的channel后调用每个channel的handleEvent()函数,对发生的事件进行处理。

  • update 2
    将TcpServer中的消息放到Acceptor和TcpConnection中处理,Acceptor中m_newConnectionCallback为TcpServer::newConnection的回调函数。listenHandler中处理连接事件,Acceptor与TcpServer用的是同一个epollfd,所以listenChannel可以在TcpServer中统一管理,在epoll中收集完活动的channel后调用活动channel的handleEvent,在listenChannel的handleEvent中会调用Acceptor::listenHandler(),listenHandler()中会调用TcpServer::newConnection()来创建一个新的TcpConnection。TcpConnection负责Channel的创建,TcpConnection::readHandler为Channel的消息回调函数。

  • update 3
    将TcpServer的for循环移到EventLoop中,EventLoop的主要成员由m_quit和m_epoller,其中m_quit用于控制循环结束,epoller用于收集活动的channel。在EventLoop::loop中有局部变量active_channels用于收集活动的channel,将active_channels传给Epoller::poll收集活动的channel再调用active_channels的handleEvent函数响应事件。EventLoop::update用于注册channel,在Channel::update()中调用此函数注册channel加入epoll中。EventLoop::update的参数为channel*,为了在epoll_wait后在events中可找到对应channel要将channel*存到epoll_event.data.ptr中。
    Epoller是epollfd的封装,主要成员由m_epollfd和epoll_event数组,在poll中填充活动的channels,返回到发生的事件到channel中。
    以前使用epollfd的地方现在都使用Epoller,本来channel中在epoll中监听新的fd现在放在EventLoop的update中完成
    epoll的创建移到Epoller的构造函数中完成
    一个连接的创建过称为:Acceptor调用TcpServer::newConnection(),TcpConnection创建Channel,并调用Channel::enableReading(),enableReading()设置监听的事件类型并调guanxi用Channel::update(),Channel::update()调动EventLoop::update()传入this指针,epoll中的注册。
    在使用时要先创建一个EventLoop,在创建TcpServer时要传入EventLoop的指针,TcpServer.start()后调用EventlLoop::loop进行消息循环。

  • update 4
    增加了用户实现的readhandler,和connectionHandler,readhandler的类型为void (TcpConnection* conn, const string& message),void connecionHandler(TcpConnection* conn),用户用TcpServer::setReadHandler和TcpServer::setConnectionHandler设置readhandler和connectionhandler,readHandler和connectionHandler都在TcpConnection中调用,readHandler的调用顺序为:Channel::handleEvent,调用回调函数TcpConnection::readHandler,TcpConnection读取消息,将this指针和消息传入用户定义的消息处理函数m_readHandler开始处理客户的事务。connectionHandler在TcpServer::newConnection中设置,TcpServer::newConnection调用TcpConnection::connectionEstablish调用connectionHandler。

  • update 5
    在TcpConnection中加入输入缓冲区和输出缓冲区,在TcpConnection::readHandler中获取消息,加入到输入缓冲区中,在用户定义的readhandler中传入输入缓冲区,让用户处理。
    在TcpConnection::send中加入对系统缓冲区满的处理,当output缓冲区为空时,直接调用write函数发送消息,如果消息没有发送完整,加未发送的消息加入到output缓冲区中,设置channel关注EPOLLOUT事件。如果output缓冲区不为空,则将要发送的消息加入output缓冲区,这样做的原因是保证保证发送消息顺序的正确性。
    在channel中加入了enableWriting()和disableWriting()函数,分为关注EPOLLOUT事件和取消关注EPOLLOUT事件,在缓冲区不为空时enableWriting(),当在TcpConnection::writeHandler中缓冲区的消息发送完毕时取消关注EPOLLOUT事件,避免没有消息发送与接收的channel也处于活动状态,降低了处理事件的效率。(TcpConnection::writeHandler为Channel的处理EPOLLOUT时的回调函数)
    在Channel增加了kNew和kAdded这两种status,在Epoller::update中会根据Channel的status来决定在epoll_ctl中使用EPOLL_CTL_ADD或者EPOLL_CTL_MOD。

什么时候会出现EPOLLOUT事件?和level triger有无关系?
在对活动的fd进行处理的过程中,唤醒epoll某一个fd的会发生什么(比如eventfd)?
ans:在处理完这一轮epoll的事件后,下一次epoll_wait马上返回

  • update 6
    加入了Buffer类,为string的简单封装,待完善
    加入了writecomplete函数,在发完消息缓冲区为空的时候触发,调用EventLopp::queueInLoop()函数,该函数将writecomplete放到回调函数队列m_pendingFunctors中进行处理,随后调用wakeUp()唤醒EventLoop,EventLoop在处理完Channel上的事件后,调用m_pendingFunctors中的函数。其中wakeUp()使用eventfd实现,用于通知EventLoop有新的事件要处理,EventLoop新增了m_wakeUpChannel,在wakeUp()中对eventfd进行write操作,在Epoller收集到活跃的m_wakeUpChannel调用它的m_readHandler(EventLoop::readHandler)来对eventfd进行read操作。
    在调用writeComplete时,如果函数中有出现send(),会出现死循环。(send中发完了消息调用writecomplete,writecomplete继续调用send不断循环)

  • update 7
    加入了Timer,Timer中的成员变量有TimeStamp m_stamp,用来记录Timer过期的时间,如果Timer是可重复的,那么m_interval就不为0。如果Timer到期了,就会调用m_timerCallback;
    TimerQueue用来是一个Timer的队列,用set保存Timer。TimerQueue中保存了m_timerfd,当TimerQueue中有ier到期的时候就会唤醒Epoller,Epoller会处理m_timerfdChannel中的事件,此时会调用TimerQueue::readHandler。在readHandler中首先要做的是读取timerfd,因为使用的是EPOLL LT,如果不读取下次还会唤醒。然后通过getExpired来获取过期的Timer的集合,在getExpired中,利用了set会根据TimeStamp排序的特点来获取m_timers中到期的Timer,然后把这些Timer删掉。得到到期的Timer的集合后会遍历这些Timer,调用它们的回调函数。最后调用reset函数,reset函数主要做的工作是如果Timer的集合中的Timer是repeat的,就把调用Timer::moveToNext所获得的下个interval的Timer插入m_timers中,最后通过调用resetTimerfd来将m_timers中最先过期的时间作为下次唤醒m_timerChannel的时间,并开始计时。
    在TimerQueue::addTimer中会创建一个新的timer,并通过TimerQueue中的EventLoop的指针来调用queueInLoop,将doAddTimer作为回调函数交给EventLoop异步执行,在doAddTimer中做的工作主要是将新的Timer插入到m_timers中,insert函数会返回最先到期的Timer是否改变了,如果改变了就要通过resetTimerfd()来重置TimerQueue到期的时间。
    在EventLoop中添加了runAt(), runAfter(), runEvery()这三个函数,runAt()的作用是传入一个TimeStamp,当到了这个TimeStamp后传入的回调函数就会执行。runAfter()的作用是传入要延迟时间,当到了延迟的时间后回调函数就会执行。runEvery的作用是传入interval,每过一个interval后回调函数都会执行一次。
    ps. Timer的管理还存在内存泄露的现象

timeval中tv_usec的时间单位?

目前存在的问题

压力测试中当并发数超出一个阈值时,消息的会处理的特别慢,这可能是main Reactor处理新连接造成的。处理新连接时涉及到Reactor的分配,队列的同步会大大地降低性能,在高并发时这个问题会变得更加明显。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值