muduo中的TcpServer一次完整的工作(上)

模拟单线程情况下muduo库的工作情况

muduo的源代码对于一个初学者来说还是有一些复杂的,其中有很多的回调函数以及交叉的组件,下面我将追踪一次TCP连接过程中发生的事情,不会出现用户态的源码,都是库内部的运行机制。下文笔者将描述一次连接发生的过程,将Channel到加入到loop循环为止。

监听套接字加入loop循环的完整过程

  • 首先创建一个TcpServer对象,在的创建过程中,首先new出来自己的核心组件(Acceptor,loop,connectionMap,threadPoll)之后TcpServer会向Acceptor注册一个新连接到来时的Connection回调函数。loop是由用户提供的,并且在最后向Acceptor注册一个回调对象,用于处理:一个新的Client连接到来时该怎么处理。

TcpServer向Acceptor注册的回调代码主要作用是:当一个新的连接到来时,根据Acceptor创建的可连接描述符和客户的地址,创建一个Connection对象,并且将这个对象加入到TcpServer的ConnectionMap中,由TcpServer来管理上述新建con对象。但是现在监听套接字的事件分发对象Channel还没有加入loop,就先不多提这个新的连接到到来时的处理过程。


TcpServer::TcpServer(EventLoop* loop,const InetAddress& listenAddr,const string& nameArg,Option option)

    : loop_(CHECK_NOTNULL(loop)),

      ipPort_(listenAddr.toIpPort()),name_(nameArg),acceptor_(new Acceptor(loop, listenAddr, option == kReusePort)),

    threadPool_(new EventLoopThreadPool(loop, name_)),

    connectionCallback_(defaultConnectionCallback),

    messageCallback_(defaultMessageCallback),

    nextConnId_(1)

{
  //上面的loop是用户提供的loop

  acceptor_->setNewConnectionCallback(

      boost::bind(&TcpServer::newConnection, this, _1, _2));//注册给acceptor的回调

}//将在Acceptor接受新连接的时候




void TcpServer::newConnection(int sockfd, const InetAddress& peerAddr)

{
  //将本函数注册个acceptor

  loop_->assertInLoopThread();//断言是否在IO线程

  EventLoop* ioLoop = threadPool_->getNextLoop();//获得线程池中的一个loop

  char buf[64];//获得线程池map中的string索引

  snprintf(buf, sizeof buf, "-%s#%d", ipPort_.c_str(), nextConnId_);

  ++nextConnId_;

  string connName = name_ + buf;



  LOG_INFO << "TcpServer::newConnection [" << name_

           << "] - new connection [" << connName

           << "] from " << peerAddr.toIpPort();

  InetAddress localAddr(sockets::getLocalAddr(sockfd));//获得本地的地址,用于构建Connection

  // 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));//构建了一个connection

  connections_[connName] = conn;//将新构建的con加入server的map中

  conn->setConnectionCallback(connectionCallback_);//muduo默认的

  conn->setMessageCallback(messageCallback_);//moduo默认的

  conn->setWriteCompleteCallback(writeCompleteCallback_);//??

  conn->setCloseCallback(

      boost::bind(&TcpServer::removeConnection, this, _1)); // FIXME: unsafe

  ioLoop->runInLoop(boost::bind(&TcpConnection::connectEstablished, conn));//在某个线程池的loop中加入这个con

}
  • 下面接着讲述在TcpServer的构造过程中发生的事情:创建Acceptor对象。TcpServer用unique_ptr持有唯一的指向Acceptor的指针。Acceptor的构造函数完成了一些常见的选项。最后的一个向Acceptor->Channel注册一个回调函数,用于处理:listening可读时(新的连接到来),该怎么办?答案是:当新的连接到来时,创建一个已连接描述符,然后调用TcpServe注册给Acceptor的回调函数,用于处理新的连接。

Acceptor::Acceptor(EventLoop* loop, const InetAddress& listenAddr, bool reuseport)

  : loop_(loop),

    acceptSocket_(sockets::createNonblockingOrDie(listenAddr.family())),

    acceptChannel_(loop, acceptSocket_.fd()),

    listenning_(false),

    idleFd_(::open("/dev/null", O_RDONLY | O_CLOEXEC))

{

  assert(idleFd_ >= 0);

  acceptSocket_.setReuseAddr(true);

  acceptSocket_.setReusePort(reuseport);

  acceptSocket_.bindAddress(listenAddr);

  acceptChannel_.setReadCallback(

      boost::bind(&Acceptor::handleRead, this));//Channel设置回调,当sockfd可读时掉用设置的回调

}




void Acceptor::handleRead()

{

  loop_->assertInLoopThread();//判断是否在IO线程

  InetAddress peerAddr;//客户的地址

  //FIXME loop until no more

  int connfd = acceptSocket_.accept(&peerAddr);//获得连接的描述符

  if (connfd >= 
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值