muduo库剖析(1)

58 阅读3分钟

我报名参加金石计划1期挑战——瓜分10万奖池,这是我的第1篇文章,点击查看活动详情

muduo网络库

项目地址:github.com/huerni/cgmu…

muduo是一个基于非阻塞IO和事件驱动的C++网络库,采用Reactor模式和one loop per thread + thread pool线程模型。
在这里插入图片描述

类功能介绍

Channel.*Poller.*EventLoop.* 三类为一体,在底层负责事件循环。EventLoop包含ChannelPollerEventLoop负责轮询访问Poller,得到激活Channel列表,使Channel自己根据自身情况调用相应回调。

Thread.*EventLoopThread.*EventLoopThreadPool.*将线程与EventLoop绑定在一起,实现one loop per thread + thread pool线程模型。

TcpServer.*Acceptor.*TcpConnection.*为上层Reactor模式的实现。TcpServer面向用户,可由用户自定义回调方法以及启动服务器。Acceptor也看做分发器,当有新连接到来时,用来将连接转发给子线程处理。TcpConnection在子线程处理被分来的连接的具体事件。

执行流程

在这里插入图片描述

  1. 用户创建TcpServer对象调用start()开启服务器,启动线程池,启动一个baseLoop(主线程),由runInLoop()处理Acceptor(Reactor)的listen()方法,进行监听操作;
  2. 一旦有客户端请求连接,Accetor调用TcpServer设置的newConnectionCallback_ ,将请求分发给subLoop(子线程),即从线程池中取出,然后将连接丢给子线程,由TcpConnection处理,此后该连接的所有操作都由该子线程独自处理,与其他线程无关。
  3. TcpConnection将读,写,关闭,错误操作与回调填充至绑定的Channel,然后调用runInLoop()queueInLoop()由底层事件循环处理。
  4. 底层事件循环中,主线程称为baseLoop,只执行监听操作与用户回调。而子线程具体执行操作由Channel具体返回的事件所执行。由图所示,蓝色部分由三类组成,EventLoopChannelPoller。每个EventLoop分别与各自的线程绑定,同时将PollerChannel连接起来,调用loop()方法将Channel的结果送入Poller执行,将Poller返回的结果各自送入Channel执行。
核心模块
Socket
Buffer
Channel
Poller与Epoller
EventLoop与EventLoopThread
EventLoopThreadPool
Acceptor
TcpConnection
TcpServer

TcpServer类

TcpServer类面向用户,用户在编码服务器时,对TcpServer类进行创建和设置。

TcpServer类包含如下数据成员:

private: 
	using ConnectionMap = std::unordered_map<std::string, TcpConnectionPtr>;
	// 首先TcpServer含有一个baseLoop,此Loop绑定主线程,只负责监听操作。
    EventLoop *loop_; // baseloop
    
	// 服务器信息
    const std::string ipPort_;
    const std::string name_;
	
	// 含有acceptor,类似于分发起,封装监听操作
    std::unique_ptr<Acceptor> acceptor_;
    
	// 服务器含有的线程池
    std::shared_ptr<EventLoopThreadPool> threadPool_;
	
	// 连接回调,读写回调,由用户编写
    ConnectionCallback connectionCallback_;
    MessageCallback messageCallback_;
    WriteCompleteCallback writeCompleteCallback_;
	
	// 线程初始化时调用回调,可选
    ThreadInitCallback threadInitCallback_;
	
	// 标志是否启动
    std::atomic_int started_;
	
	// 保存所有连接
    int nextConnId_;
    ConnectionMap connections_;

用户创建TcpServer对象时,需指定baseLoop,需要监听的套接字,并设置自定义的回调和线程池中需要初始化的线程个数。

TcpServer::TcpServer(EventLoop* loop, 
                    const InetAddress &listenAddr, 
                    const std::string &nameArg, 
                    Option option)
                : loop_(CheckLoopNotNull(loop))
                , ipPort_(listenAddr.toIpPort())
                , name_(nameArg)
                , acceptor_(new Acceptor(loop, listenAddr, option == kReusePort))
                , threadPool_(new EventLoopThreadPool(loop, name_))
                , connectionCallback_()
                , messageCallback_()
                , nextConnId_(1)
                , started_(0) 
{
    // 传入新连接到来操作回调到acceptor
    acceptor_->setNewConnectionCallback(std::bind(&TcpServer::newConnection, this, 
        std::placeholders::_1, std::placeholders::_2));
}

void setThreadInitcallback(const ThreadInitCallback &cb) { threadInitCallback_ = cb; }
void setConnectionCallback(const ConnectionCallback &cb) { connectionCallback_ = cb; }
void setMessageCallback(const MessageCallback &cb) { messageCallback_ = cb; }
void setWriteCompleteCallback(const WriteCompleteCallback &cb) { writeCompleteCallback_ = cb; }

void setThreadNum(int numThreads);

设置好后,调用start()开启服务器

void TcpServer::start() {
    if(started_++ == 0) {
        // 开启线程池
        threadPool_->start(threadInitCallback_);
        // 由loop轮询运行acceptor封装的listen函数
        loop_->runInLoop(std::bind(&Acceptor::listen, acceptor_.get()));
    }
}

当新连接到来时,会调用TcpServer中的newConnection方法(怎么调用?可查看Acceptor.*代码)

void TcpServer::newConnection(int sockfd, const InetAddress &peerAddr) {
    // 从线程池中获取空闲线程
    EventLoop *ioLoop = threadPool_->getNextLoop();
    char buf[64] = {0};
    snprintf(buf, sizeof buf, "-%s#%d", ipPort_.c_str(), nextConnId_);
    ++nextConnId_;
    std::string connName = name_ + buf;

    LOG_INFO("TcpServer::newConnection [%s] - new connection [%s] from %s \n", 
    name_.c_str(), connName.c_str(), peerAddr.toIpPort().c_str());

    sockaddr_in local;
    ::bzero(&local, sizeof local);
    socklen_t addrlen = sizeof local;
    if(::getsockname(sockfd, (sockaddr*)&local, &addrlen) < 0) {
        LOG_ERROR("sockets::getLocalAddr");
    }
    InetAddress localAddr(local);
	// 创建TcpConnection对象,并将自定义回调传入
    TcpConnectionPtr conn(new TcpConnection(
                            ioLoop,
                            connName,
                            sockfd,
                            localAddr,
                            peerAddr));

    connections_[connName] = conn;
    conn->setConnectionCallback(connectionCallback_);
    conn->setMessageCallback(messageCallback_);
    conn->setWriteCompleteCallback(writeCompleteCallback_);

    conn->setCloseCallback(
        std::bind(&TcpServer::removeConnection, this, std::placeholders::_1)
    );
	// 丢入loop中轮询连接建立完成回调
    ioLoop->runInLoop(std::bind(&TcpConnection::connectEstableished, conn));
}