Muduo(C++11版本) 源码剖析(六)———TcpServer和Acceptor设计

TcpServer主要功能是管理新的连接到来时创建的TcpConnection,是直接提供给用户使用的类,生命周期由用户控制。Acceptor是创建在TcpServer内部的对象,内部会有一个Channel来专门处理新连接到来事件,新连接到来最终会回调给TcpServer的newconnection来创建新连接,其实前几期muduo内部的机制都讲完了,如果能够理解前面的部分,这两个类基本看下就能知道作用了。

TcpServer

成员变量:

    private:
		EventLoop*                                      loop_; // the acceptor loop
		const string                                    hostport_;
		const string                                    name_;
		std::unique_ptr<Acceptor>                       acceptor_; 
                std::unique_ptr<EventLoopThreadPool>            eventLoopThreadPool_;
		ConnectionCallback                              connectionCallback_;
		MessageCallback                                 messageCallback_;//回调函数处理消息
		WriteCompleteCallback                           writeCompleteCallback_;
		ThreadInitCallback                              threadInitCallback_;
		std::atomic<int>                                started_; 
		int                                             nextConnId_;
		ConnectionMap                                   connections_;

loop是主线程循环,hostport是传入的需要监听的端口,name是给服务端标记的名字,acceptor是一个新连接接受的对象,eventLoopThreadPool是线程池对象,之后创建了几个回调函数,回调了一些函数来处理一些自定义的消息,之后started_是线程池启动的标志位,nextConnid是标记连接的序号,最后的ConnectionMap是一个存放TcpConnection的容器。

 

重要函数及作用:

创建

TcpServer::TcpServer(EventLoop* loop,
    const InetAddress& listenAddr,
    const std::string& nameArg,
    Option option)
    : loop_(loop),
    hostport_(listenAddr.toIpPort()),
    name_(nameArg),
    acceptor_(new Acceptor(loop, listenAddr, option == kReusePort)),
    //threadPool_(new EventLoopThreadPool(loop, name_)),
    connectionCallback_(defaultConnectionCallback),
    messageCallback_(defaultMessageCallback),
    started_(0),
    nextConnId_(1)
{
    acceptor_->setNewConnectionCallback(std::bind(&TcpServer::newConnection, this, std::placeholders::_1, std::placeholders::_2));
}

可以看到,在构造函数参数中传入了主线程循环,监听socket地址信息,初始化参数列表中用传入的参数和一些固定值初始化了一些变量,new了一个新连接接受处理类acceptor,设置了外部回调,然后在函数体中注册了一个这个类最关键的回调——新连接回调。

启动线程池

void TcpServer::start(int workerThreadCount/* = 4*/)
{
    if(started_ == 0) //没有启动
    {
		//启动线程池管理
        eventLoopThreadPool_.reset(new EventLoopThreadPool());
        eventLoopThreadPool_->init(loop_, workerThreadCount);
        eventLoopThreadPool_->start();
        
        //threadPool_->start(threadInitCallback_);
        //assert(!acceptor_->listenning());

		//将函数对象放入队列或者立即执行
        loop_->runInLoop(std::bind(&Acceptor::listen, acceptor_.get()));//监听端口并注册进多路复用
        started_ = 1;
    }
}

创建线程池,在线程池中会根据workerThreadCount创建对应线程,之后在当前循环(主线程循环)中进行端口的监听以及注册,置启动标志位为1。

新连接到来

void TcpServer::newConnection(int sockfd, const InetAddress& peerAddr)
{
    loop_->assertInLoopThread();
	//给新连接分配线程
    EventLoop* ioLoop = eventLoopThreadPool_->getNextLoop();
    char buf[32];
    snprintf(buf, sizeof buf, ":%s#%d", hostport_.c_str(), nextConnId_);
    ++nextConnId_;
    string connName = name_ + buf;

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

    InetAddress localAddr(sockets::getLocalAddr(sockfd));
    // FIXME poll with zero timeout to double confirm the new connection
    // FIXME use make_shared if necessary
    TcpConnectionPtr conn(new TcpConnection(ioLoop, connName, sockfd, localAddr, peerAddr));
	//放入map容器管理
    connections_[connName] = conn;
    conn->setConnectionCallback(connectionCallback_);//指向业务层Session
    conn->setMessageCallback(messageCallback_);
    conn->setWriteCompleteCallback(writeCompleteCallback_);
    conn->setCloseCallback(std::bind(&TcpServer::removeConnection, this, std::placeholders::_1)); // FIXME: unsafe
    //该线程分离完io事件后,立即调用TcpConnection::connectEstablished
    ioLoop->runInLoop(std::bind(&TcpConnection::connectEstablished, conn));
}

由Accepter检测到新连接回调到TcpServer::newConnection,创建相应的TcpConnection并传入eventloop,对端地址簇,本段地址簇等信息,最后将TcpConnection传入Map容器,并向其注册一些回调函数。最后在对应线程的eventloop中调用连接建立函数。

移除连接

void TcpServer::removeConnection(const TcpConnectionPtr& conn)
{
    // FIXME: unsafe
    loop_->runInLoop(std::bind(&TcpServer::removeConnectionInLoop, this, conn));
}

void TcpServer::removeConnectionInLoop(const TcpConnectionPtr& conn)
{
	//主线程执行
    loop_->assertInLoopThread();
    LOGD("TcpServer::removeConnectionInLoop [%s] - connection %s", name_.c_str(), conn->name().c_str());
    size_t n = connections_.erase(conn->name());  //引用计数减1,当前应该是2
    //(void)n;
    //assert(n == 1);
    if (n != 1)
    {
        //出现这种情况,是TcpConneaction对象在创建过程中,对方就断开连接了。
        LOGD("TcpServer::removeConnectionInLoop [%s] - connection %s, connection does not exist.", name_.c_str(), conn->name().c_str());
        return;
    }
    EventLoop* ioLoop = conn->getLoop();
    ioLoop->queueInLoop(std::bind(&TcpConnection::connectDestroyed, conn));

	//执行完这个函数 会释放handleclose栈上对象 引用计数-1
	//当前应该是1
}

当连接断开时通过对应Channel的HandleEvent最终会调用到removeConnection函数,在函数中将removeConnectionInLoop加入主线程执行队列,之后在主线程执行removeConnectionInLoop函数。在removeConnectionInLoop中将需要断开连接的TcpConnection从map容器中移除,再在对应的线程eventloop中将其销毁,关于TcpConnection的生命周期还请看上篇博客。

结束

void TcpServer::stop()
{
    if (started_ == 0)
        return; 
    for (ConnectionMap::iterator it = connections_.begin(); it != connections_.end(); ++it)
    {
        TcpConnectionPtr conn = it->second;
        it->second.reset();
        conn->getLoop()->runInLoop(std::bind(&TcpConnection::connectDestroyed, conn));
        conn.reset();
    }
    eventLoopThreadPool_->stop();
    started_ = 0;
}

结束线程池,主动关闭销毁每个连接,没啥好讲的。

 

Acceptor

成员变量:

    private:
        EventLoop*            loop_;
        Socket                acceptSocket_; //用于服务端监听的真正的socket
        Channel               acceptChannel_;
        NewConnectionCallback newConnectionCallback_;
        bool                  listenning_;

loop是主线程循环,也从TcpServer中传入loop,acceptSocket是服务端监听socket,Channel是专门用于接受连接事件,放在了主IO线程的通道,newConnectionCallback_是注册回调,指向了TcpServer::newConnection(),listenning是监听中标志位。

重要函数及作用:

初始化

Acceptor::Acceptor(EventLoop* loop, const InetAddress& listenAddr, bool reuseport)
    : loop_(loop),
    acceptSocket_(sockets::createNonblockingOrDie()),
    acceptChannel_(loop, acceptSocket_.fd()),
    listenning_(false)   
{
#ifndef _WIN64
    idleFd_ = ::open("/dev/null", O_RDONLY | O_CLOEXEC);
#endif

    acceptSocket_.setReuseAddr(true);
    acceptSocket_.setReusePort(reuseport);
    acceptSocket_.bindAddress(listenAddr);
    acceptChannel_.setReadCallback(std::bind(&Acceptor::handleRead, this));
}

由TcpServer创建,传入主线程循环loop,监听地址信息等参数,参数初始化列表中构造了acceptSocket和acceptChannel,函数体中设置socket信息以及设置了Channel到TcpServer的回调函数。

监听

void Acceptor::listen()
{
    loop_->assertInLoopThread();
    listenning_ = true;
    acceptSocket_.listen();          //监听对应端口
    acceptChannel_.enableReading();  //注册到poll中
}

开始监听端口,并将acceptChannel注册到IO复用poll中去。

处理新连接事件

void Acceptor::handleRead()
{
    loop_->assertInLoopThread();
    InetAddress peerAddr;
    //FIXME loop until no more
    int connfd = acceptSocket_.accept(&peerAddr); //返回创建的新的套接字
    if (connfd >= 0)
    {
         string hostport = peerAddr.toIpPort();
         LOGD("Accepts of %s", hostport.c_str());
        //newConnectionCallback_实际指向TcpServer::newConnection(int sockfd, const InetAddress& peerAddr)
        if (newConnectionCallback_)
        {
            newConnectionCallback_(connfd, peerAddr);
        }
        else
        {
            sockets::close(connfd);
        }
    }
    else
    {
        LOGSYSE("in Acceptor::handleRead");
#ifndef _WIN64
        if (errno == EMFILE)
        {
            ::close(idleFd_);
            idleFd_ = ::accept(acceptSocket_.fd(), NULL, NULL);
            ::close(idleFd_);
            idleFd_ = ::open("/dev/null", O_RDONLY | O_CLOEXEC);
        }
#endif
    }
}

由IO多路复用检测到事件,通过Channel处理可读事件,回调到该函数的handleRead(),最终触发TcpServer::newConnection创建新连接。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值