muduo库分析——net篇(1)EventLoop

本文深入解析了事件驱动架构的核心组件,包括Channel、EventLoop和Poller的作用与交互方式。阐述了如何通过Channel注册和响应IO事件,EventLoop如何管理事件循环,以及Poller如何监控多个文件描述符的IO事件。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

类图:


可以看出大概的关系

Channel负责注册删除和响应的IO事件,具有IO当前状态,持有相关的回调函数

EventLoop具有事件循环能力,从Poller中不停的读取IO事件,反馈到Channel上,执行Channel的回调函数

Poller持有Channel的map结构,形式为<int fd,Channel*>,其派生类会新增相关文件描述符集合(用于从系统调用中读取相关IO事件)



EventLoop.cpp

  wakeupChannel_->setReadCallback(
      boost::bind(&EventLoop::handleRead, this));
  // we are always reading the wakeupfd
  wakeupChannel_->enableReading();

在构造函数中进行IO事件注册,wakeupChannel用于线程唤醒,当其他线程要关闭当前线程Loop时,调用quit函数,使用wakeupChannel发送数据,阻塞在poller_->poll(kPollTimeMs, &activeChannels_);时候就会获得返回并执行下去,退出while循环

while (!quit_)
  {
    activeChannels_.clear();	//清理活跃中的IO通道
    pollReturnTime_ = poller_->poll(kPollTimeMs, &activeChannels_);	//等待Poller将活跃的IO通道传送过来
    ++iteration_;
    if (Logger::logLevel() <= Logger::TRACE)
    {
      printActiveChannels();	//输出通道
    }
    // TODO sort channel by priority
    eventHandling_ = true;	//处理事件状态
    for (ChannelList::iterator it = activeChannels_.begin();	//遍历每个IO通道处理相关事件
        it != activeChannels_.end(); ++it)
    {
      currentActiveChannel_ = *it;
      currentActiveChannel_->handleEvent(pollReturnTime_);
    }
    currentActiveChannel_ = NULL;
    eventHandling_ = false;
    doPendingFunctors();
  }
void EventLoop::updateChannel(Channel* channel)	//更新Poller中Channel对应文件描述符状态
{
  assert(channel->ownerLoop() == this);
  assertInLoopThread();
  poller_->updateChannel(channel);
}

Channel.h和Channel.cpp

  EventLoop* loop_;	//持有事件循环指针
  const int  fd_;	//持有文件描述符ID
  int        events_;	//感兴趣的事件
  int        revents_; // it's the received event types of epoll or poll 获得的事件反馈
  int        index_; // used by Poller.	EPoller中代表当前IO活动状态,Poller中代表当前IO所在Poller文件描述符数组的下标位置
  bool       logHup_;

  boost::weak_ptr<void> tie_;	
  bool tied_;
  bool eventHandling_;
  bool addedToLoop_;
  ReadEventCallback readCallback_;	//事件回调函数
  EventCallback writeCallback_;
  EventCallback closeCallback_;
  EventCallback errorCallback_;
void Channel::handleEvent(Timestamp receiveTime)
{
  boost::shared_ptr<void> guard;
  if (tied_)
  {
    guard = tie_.lock();
    if (guard)
    {
      handleEventWithGuard(receiveTime);
    }
  }
  else
  {
    handleEventWithGuard(receiveTime);
  }
}
void Channel::handleEventWithGuard(Timestamp receiveTime)
上面是Channel调用函数的过程,根据revents的状态码执行响应的动作

Poller.h和Poller.cpp

 protected:
  typedef std::map<int, Channel*> ChannelMap;	
  ChannelMap channels_;		// fd,Channel 集合
  virtual Timestamp poll(int timeoutMs, ChannelList* activeChannels) = 0;

  /// Changes the interested I/O events.
  /// Must be called in the loop thread.
  virtual void updateChannel(Channel* channel) = 0;

  /// Remove the channel, when it destructs.
  /// Must be called in the loop thread.
  virtual void removeChannel(Channel* channel) = 0;

  virtual bool hasChannel(Channel* channel) const;

  static Poller* newDefaultPoller(EventLoop* loop);
几个派生类需要实现的函数,EventLoop通过基类指针调用派生类函数



### 关于 Muduo 中的 `TCPClient` 使用方法 #### 创建并初始化 TCP 客户端实例 为了创建一个基于 Muduo 的 TCP 客户端,首先需要定义一个继承自 `muduo::net::TcpConnection::EstablishCallback` 和其他必要回调接口的类来处理连接建立后的逻辑。接着通过指定远程服务器地址和监听本地事件循环的方式构建 `TcpClient` 对象。 ```cpp #include <muduo/net/TcpClient.h> #include <muduo/net/EventLoop.h> using namespace muduo; using namespace muduo::net; class Client : boost::noncopyable { public: explicit Client(EventLoop* loop, const InetAddress& serverAddr) : client_(loop, serverAddr, "TestClient") { client_.setConnectionCallback( std::bind(&Client::onConnection, this, _1)); client_.setMessageCallback( std::bind(&Client::onMessage, this, _1, _2, _3)); } private: TcpClient client_; void onConnection(const TcpConnectionPtr& conn){ LOG_INFO << conn->localAddress().toIpPort() << " -> " << conn->peerAddress().toIpPort() << " is " << (conn->connected() ? "UP" : "DOWN"); } void onMessage(const TcpConnectionPtr&, Buffer*, Timestamp) {} }; ``` 上述代码展示了如何设置当新连接被成功建立时触发的方法以及接收到数据包之后的操作[^1]。 #### 启动客户端尝试连接到服务端 一旦完成了 `TcpClient` 实例化及其关联行为设定,则可以通过调用其成员函数 connect 来发起至目标主机的服务请求: ```cpp int main(int argc, char *argv[]) { if (argc != 2) { fprintf(stderr, "Usage: %s host_ip\n", argv[0]); exit(1); } EventLoop loop; InetAddress serverAddr(argv[1], 8888); Client client(&loop, serverAddr); client.connect(); loop.loop(); } ``` 这段程序片段显示了启动应用程序所需的主要流程——解析命令行参数获取目的 IP 地址、配置好相应的套接字选项后执行实际链接操作,并进入主事件轮询等待进一步指令或交互过程的发生。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值