【muduo库学习】实现最简单的reactor模式

来源:互联网 发布:mac os x 10.11 下载 编辑:程序博客网 时间:2024/05/21 17:15

《linux多线程服务端编程 使用muduo c++网络库》的第8章是从0开始讲述一个网络库的实现,比较适合初学者入门。

 在本书的第8章中是实现了以下几个类:eventloop类,poller类,channel类

首先分析channel类的定义:

#ifndef MUDUO_NET_CHANNEL_H#define MUDUO_NET_CHANNEL_H#include <boost/function.hpp>#include <boost/noncopyable.hpp>namespace muduo{class EventLoop;////// A selectable I/O channel.////// This class doesn't own the file descriptor./// The file descriptor could be a socket,/// an eventfd, a timerfd, or a signalfdclass Channel : boost::noncopyable{ public:  typedef boost::function<void()> EventCallback;  Channel(EventLoop* loop, int fd);  void handleEvent();  void setReadCallback(const EventCallback& cb)  { readCallback_ = cb; }  void setWriteCallback(const EventCallback& cb)  { writeCallback_ = cb; }  void setErrorCallback(const EventCallback& cb)  { errorCallback_ = cb; }  int fd() const { return fd_; }  int events() const { return events_; }  void set_revents(int revt) { revents_ = revt; }  bool isNoneEvent() const { return events_ == kNoneEvent; }  void enableReading() { events_ |= kReadEvent; update(); }  // void enableWriting() { events_ |= kWriteEvent; update(); }  // void disableWriting() { events_ &= ~kWriteEvent; update(); }  // void disableAll() { events_ = kNoneEvent; update(); }  // for Poller  int index() { return index_; }  void set_index(int idx) { index_ = idx; }  EventLoop* ownerLoop() { return loop_; } private:  void update();  static const int kNoneEvent;  static const int kReadEvent;  static const int kWriteEvent;  EventLoop* loop_;  const int  fd_;  int        events_;  int        revents_;  int        index_; // used by Poller.  EventCallback readCallback_;  EventCallback writeCallback_;  EventCallback errorCallback_;};}#endif  // MUDUO_NET_CHANNEL_H
fd_是channel类的私有变量,一个channel对象是跟文件句柄fd直接挂钩的。channel类的构造函数是需要loop 和 fd两个参数,因此每一个关注的fd都会创建一个专门的channel对象来对其负责。

接下来看看poller类的定义:

class Poller : boost::noncopyable{ public:  typedef std::vector<Channel*> ChannelList;  Poller(EventLoop* loop);  ~Poller();  /// Polls the I/O events.  /// Must be called in the loop thread.  Timestamp poll(int timeoutMs, ChannelList* activeChannels);  /// Changes the interested I/O events.  /// Must be called in the loop thread.  void updateChannel(Channel* channel);  void assertInLoopThread() { ownerLoop_->assertInLoopThread(); } private:  void fillActiveChannels(int numEvents,                          ChannelList* activeChannels) const;  typedef std::vector<struct pollfd> PollFdList;  typedef std::map<int, Channel*> ChannelMap;  EventLoop* ownerLoop_;  PollFdList pollfds_;  ChannelMap channels_;};}#endif  // MUDUO_NET_POLLER_H

poller类的实现

#include "Poller.h"#include "Channel.h"#include "logging/Logging.h"#include <assert.h>#include <poll.h>using namespace muduo;Poller::Poller(EventLoop* loop)  : ownerLoop_(loop){}Poller::~Poller(){}Timestamp Poller::poll(int timeoutMs, ChannelList* activeChannels){  // XXX pollfds_ shouldn't change  int numEvents = ::poll(&*pollfds_.begin(), pollfds_.size(), timeoutMs);  Timestamp now(Timestamp::now());  if (numEvents > 0) {    LOG_TRACE << numEvents << " events happended";    fillActiveChannels(numEvents, activeChannels);  } else if (numEvents == 0) {    LOG_TRACE << " nothing happended";  } else {    LOG_SYSERR << "Poller::poll()";  }  return now;}void Poller::fillActiveChannels(int numEvents,                                ChannelList* activeChannels) const{  for (PollFdList::const_iterator pfd = pollfds_.begin();      pfd != pollfds_.end() && numEvents > 0; ++pfd)  {    if (pfd->revents > 0)    {      --numEvents;      ChannelMap::const_iterator ch = channels_.find(pfd->fd);      assert(ch != channels_.end());      Channel* channel = ch->second;      assert(channel->fd() == pfd->fd);      channel->set_revents(pfd->revents);      // pfd->revents = 0;      activeChannels->push_back(channel);    }  }}void Poller::updateChannel(Channel* channel){  assertInLoopThread();  LOG_TRACE << "fd = " << channel->fd() << " events = " << channel->events();  if (channel->index() < 0) {    // a new one, add to pollfds_    assert(channels_.find(channel->fd()) == channels_.end());    struct pollfd pfd;    pfd.fd = channel->fd();    pfd.events = static_cast<short>(channel->events());    pfd.revents = 0;    pollfds_.push_back(pfd);    int idx = static_cast<int>(pollfds_.size())-1;    channel->set_index(idx);    channels_[pfd.fd] = channel;  } else {    // update existing one    assert(channels_.find(channel->fd()) != channels_.end());    assert(channels_[channel->fd()] == channel);    int idx = channel->index();    assert(0 <= idx && idx < static_cast<int>(pollfds_.size()));    struct pollfd& pfd = pollfds_[idx];    assert(pfd.fd == channel->fd() || pfd.fd == -1);    pfd.events = static_cast<short>(channel->events());    pfd.revents = 0;    if (channel->isNoneEvent()) {      // ignore this pollfd      pfd.fd = -1;    }  }}


poller对象是有pollfdlist,channels这样的私有成员。在poller::updatechannel中,会把channel对象中的fd抽取出来,组成pollfd结构体,然后添加到pollfds_数组中。并且会对pollfdlist,channels等私有成员进行相应的修改,所以poller::update是将channel和poll联系起来的关键函数。

而在poller::poll中则会调用poll系统调用来完成关心事件的IO多路复用,并且将当前有活动的事件添加到activechannels中。

最后在eventloop::loop()中会调用相应事件的处理方法:

void EventLoop::loop(){  assert(!looping_);  assertInLoopThread();  looping_ = true;  quit_ = false;  while (!quit_)  {    activeChannels_.clear();    poller_->poll(kPollTimeMs, &activeChannels_);    for (ChannelList::iterator it = activeChannels_.begin();        it != activeChannels_.end(); ++it)    {      (*it)->handleEvent();    }  }  LOG_TRACE << "EventLoop " << this << " stop looping";  looping_ = false;}


通过以上几个简单的类,即可实现最简单的reactor模式。

再来看简单的测试程序:

#include "Channel.h"#include "EventLoop.h"#include <stdio.h>#include <sys/timerfd.h>muduo::EventLoop* g_loop;void timeout(){  printf("Timeout!\n");  g_loop->quit();}int main(){  muduo::EventLoop loop;  g_loop = &loop;  int timerfd = ::timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC);  muduo::Channel channel(&loop, timerfd);  channel.setReadCallback(timeout);  channel.enableReading();  struct itimerspec howlong;  bzero(&howlong, sizeof howlong);  howlong.it_value.tv_sec = 5;  ::timerfd_settime(timerfd, 0, &howlong, NULL);  loop.loop();  ::close(timerfd);}

上述程序中是将timerfd交由eventloop管理,并且编写其handler函数timeout()。eventloop会完成事件的检测和分发。




0 0