【Muduo源码分析】muduo的Tcpserver 和Acceptor 解读

本文章主要分析在Muduo网络库中,服务端如何建立连接的。

首先看Tcpserver的构造函数(本系列主要基于示例程序而言,可能与最终的Muduo版本存在出入,但总体框架一致)

TcpServer::TcpServer(EventLoop* loop, const InetAddress& listenAddr)
  : loop_(CHECK_NOTNULL(loop)),
    name_(listenAddr.toHostPort()),
    acceptor_(new Acceptor(loop, listenAddr)),
    threadPool_(new EventLoopThreadPool(loop)),
    started_(false),
    nextConnId_(1)
{
  acceptor_->setNewConnectionCallback(
      boost::bind(&TcpServer::newConnection, this, _1, _2));
}

Tcpsever在构造时,传入了一个Eventloop的指针,和一个地址结构(用作监听)。然后这两个参数在参数列表中,又去当作参数去构造一个Acceptor对象。

Acceptor这个类,顾名思义,就是负责接受客户端的连接。现在看看Acceptor的构造

Acceptor::Acceptor(EventLoop* loop, const InetAddress& listenAddr)
  : loop_(loop),
    acceptSocket_(sockets::createNonblockingOrDie()),
    acceptChannel_(loop, acceptSocket_.fd()),
    listenning_(false)
{
  acceptSocket_.setReuseAddr(true);
  acceptSocket_.bindAddress(listenAddr);
  acceptChannel_.setReadCallback(
      boost::bind(&Acceptor::handleRead, this));
}

Acceptor的对象创建时,会创建一个非阻塞的套接字,然后为这个套接字同时创建一个Channel,将acceptSocket_绑定在channel上。

在构造函数的函数体内,将Acceptor的handleRead函数作为acceptChannel_的可读事件回调函数,进行注册。(Channel回调函数的注册见Channel的解析,目前还没写哈,日后补上)

接下来看看这个handleRead函数

void Acceptor::handleRead()
{
  loop_->assertInLoopThread();
  InetAddress peerAddr(0);
  //FIXME loop until no more
  int connfd = acceptSocket_.accept(&peerAddr);
  if (connfd >= 0) {
    if (newConnectionCallback_) {
      newConnectionCallback_(connfd, peerAddr);
    } else {
      sockets::close(connfd);
    }
  }
}

当handleRead可读时,一定是服务端发起了连接请求。所以handleRead被调用时,首先调用accept函数建立连接。然后再去调用newConnectionCallback_回调函数。

那么这个newConnectionCallback_回调函数是什么时候注册的呢?是在Tcpsever对象构造函数的函数体内,参看上面的代码。

Tcpserver将自己的成员函数newConnection设置为Acceptor 中handleRead的执行函数。

看看下面的newConnection。

void TcpServer::newConnection(int sockfd, const InetAddress& peerAddr)
{
  loop_->assertInLoopThread();
  char buf[32];
  snprintf(buf, sizeof buf, "#%d", nextConnId_);
  ++nextConnId_;
  std::string connName = name_ + buf;

  LOG_INFO << "TcpServer::newConnection [" << name_
           << "] - new connection [" << connName
           << "] from " << peerAddr.toHostPort();
  InetAddress localAddr(sockets::getLocalAddr(sockfd));
  // FIXME poll with zero timeout to double confirm the new connection
  EventLoop* ioLoop = threadPool_->getNextLoop();
  TcpConnectionPtr conn(
      new TcpConnection(ioLoop, connName, sockfd, localAddr, peerAddr));
  connections_[connName] = conn;
  conn->setConnectionCallback(connectionCallback_);
  conn->setMessageCallback(messageCallback_);
  conn->setWriteCompleteCallback(writeCompleteCallback_);
  conn->setCloseCallback(
      boost::bind(&TcpServer::removeConnection, this, _1)); // FIXME: unsafe
  ioLoop->runInLoop(boost::bind(&TcpConnection::connectEstablished, conn));
}

由于被handleRead调用,所以形参就是handleRead传入的连接描述符 和对方的地址结构。这里使用了EventLoopThreadPool,从中选中一个Eventloop,然后创建了Tcopconnection对象,将对象在选中的loop中执行。接着将服务端设置的各种回调函数注册到Tcpconnection中,最后就是在loop中执行Tcpconnection对象中的读写事件。

至此,Tcpsever成功建立连接,大致的整理了一下其中的过程。下图的调用过程和上述是一致的,只不过我是从Tcpsever开始,一直向下分析

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值