muduo 源码分析(一)服务端工作流程

本文分析了muduo网络库的服务端工作流程,包括TcpServer、Acceptor、TcpConnection等核心类的作用,以及启动、新连接到来、数据收发和关闭连接的步骤。详细探讨了TcpConnection的收发数据机制和关闭连接的主动与被动方式。
摘要由CSDN通过智能技术生成

目录

0.简介

1. 各个类的作用:

TcpServer 

Acceptor

TcpConnection

EventLoopThreadPool

EventLoopThread

EventLoop

Poller

Channel

2. 启动

3. 新连接到来

4. 数据收发

收数据

发数据

5. 关闭连接

主动关闭

被动关闭

参考文献


0.简介

最近才拜读了《Unix网络编程卷1》的前15章,书很有用,但也很枯燥。Talk is cheap, show me your code. 于是找了muduo这个网络库来研究研究。

muduo基于reactor模式, 是一个多线程的c++网络库,只支持linux上的tcp协议. 实现上,采用 "one thread, one loop" , 到处充斥着回调函数, 看习惯了就好。服务端大致类图如下:

1. 各个类的作用:

TcpServer 

一般运行在主线程,由用户创建和销毁。功能包括监听套接口,创建事件循环线程池,创建并保存新连接TcpConnection, 以及和用户代码交互(通过xxxCallback 函数)。

Acceptor

主要负责监听连接请求,调用listen() 接口时,通过Channel::enableReading() 把socket的描述符加到poller(I/O复用器)中。当有新连接到达时,先调用系统函数accept,再回调函数 newConnectionCallback_ 让TcpServer去创建连接。

TcpConnection

 这个类同时被服务端和客户端使用。建立连接后,通过Channel::enableReading() 把socket的描述符加到poller(I/O复用器)中,然后进行正常的TCP 数据收发。基本上一个TcpConnection对应一个Channel.

EventLoopThreadPool

管理所有客户端连接的线程池,每个线程都有唯一一个事件循环。可以调用setThreadNum设置线程的数目。

EventLoopThread

事件循环线程, 包含一个Thread对象,一个EventLoop对象。在构造函数中,把EventLoopThread::threadFunc 注册到Thread对象中(线程启动时会调用EventLoopThread::threadFunc),线程启动如下:

EventLoop* EventLoopThread::startLoop()
{
  assert(!thread_.started());
  thread_.start();  // 这个时候loop_为NULL, 然后启动线程

  {
    MutexLockGuard lock(mutex_);
    while (loop_ == NULL)
    {
      cond_.wait(); // 如果loop_为NULL,就等待, 直到线程完全启动,调用下面的threadFunc(), loop_ 被创建
    }
  }

  return loop_;
}

void EventLoopThread::threadFunc()
{
  EventLoop loop;

  if (callback_)
  {
    callback_(&loop);
  }

  {
    MutexLockGuard lock(mutex_);
    loop_ = &loop;
    cond_.notify();  // loop_创建了,通知startLoop()函数
  }

  loop.loop();
  //assert(exiting_);
  loop_ = NULL;
}

以下三个是reactor模式的核心类:

EventLoop

线程启动后,事件循环就开始跑起来。首先调用Poller:poll(阻塞调用) 接口查看I/O 可读/可写情况,获得所有活跃的channel, 执行每个channel的handleEvent函数,这个handleEvent最终会调用到Accepter或TcpConnection的handleRead / handleWrite, 完成真正的读或写。然后调用doPendingFunctors 按顺序执行 队列中的函数。

void EventLoop::loop()
{
  assert(!looping_);
  assertInLoopThread();  // loop()函数的调用者必须是EventLoop对象的创建者
  looping_ = true;
  quit_ = false;  // FIXME: what if someone calls quit() before loop() ?
  LOG_TRACE << "EventLoop " << this << " start looping";

  while (!quit_)  // 如果不退出,就一直循环
  {
    activeChannels_.clear();
    pollReturnTime_ = poller_->poll(kPollTimeMs, &activeChannels_); // 获得所有可用的channels
    ++iteration_;
    if (Logger::logLevel() <= Logger::TRACE)
    {
      printActiveChannels();
    }
    // TODO sort channel by priority
    eventHandling_ = true;
    for (ChannelList::iterator it = activeChannels_.begin();
        it != activeChannels_.end(); ++it)
    {
      currentActiveChannel_ = *it;
      currentActiveChannel_->handleEvent(pollReturnTime_);  // 执行handleEvent
    }
    currentActiveChannel_ = NULL;
    eventHandling_ = false;
    doPendingFunctors(); // 执行队列中的函数
  }

  LOG_TRACE << "EventLoop " << this << " stop looping";
  looping_ = false;
}

Poller

这是muduo中唯一的抽象类,被PollPoller和EpollPoller实现,内部分别使用Linux的poll和epoll系统函数来做I/O轮询

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值