muduo源码剖析 - TcpServer

说明

 TcpServer类,直接给用户使用的类。如果下层的那些类看明白了,这个很简单,指挥各个类的运作即可。 主要工作如下:

1. 接受acceptor的新连接, 并创建TcpConnection对象,注册TcpConnection的可读事件。

2. 接受用户三个半事件的回调:

      陈硕认为,TCP 网络编程的本质是处理三个半事件,即:

      连接的建立

      连接的断开:包括主动断开和被动断开

      消息到达,文件描述符可读。

      消息发送完毕。这个算半个事件。

成员变量

重点关注:

1. loop:主线程loop,和acceptor, io线程池是一样的loop;

2. IpPort: 监听的ip和端口

3.acceptor: 接受器,用于接受新连接。

4.threadPool_: IO线程池。

5. 四个回调,其中连接的建立和断开用同一个回调。使用tcpconnection的状态判断是连接还是断开。

6. nextId:为每个连接起一个不一样的名字。

8. connections_: 保存了所有TcpConnection的智能指针,保证连接断开前不被析构。

  EventLoop* loop_;  // the acceptor loop //主线程
  const string ipPort_; //服务端口
  const string name_;

  /*
    1、 unique_ptr去掉了拷贝构造函数和operator=赋值重载函数
        禁止用户对unique_ptr进行显示的拷贝构造和赋值,防止智能指针浅拷贝问题的发生。
    2、但是unique_ptr提供了带右值引用参数的拷贝构造和赋值
       unique_ptr智能指针可以通过右值引用进行拷贝构造和赋值操作
       或者在产生unique_ptr临时对象的地方,例如函数返回值为unique_ptr。
  */
  std::unique_ptr<Acceptor> acceptor_; // avoid revealing Acceptor   //提供监听套接字创建绑定功能,listen开始监听
  std::shared_ptr<EventLoopThreadPool> threadPool_; //线程池指针

  /*
      陈硕认为,TCP 网络编程的本质是处理三个半事件,即:
      连接的建立
      连接的断开:包括主动断开和被动断开
      消息到达,文件描述符可读。
      消息发送完毕。这个算半个事件。
  */
  ConnectionCallback connectionCallback_;
  MessageCallback messageCallback_;
  WriteCompleteCallback writeCompleteCallback_;

  ThreadInitCallback threadInitCallback_;
  AtomicInt32 started_; //判断是否开始,一开始为0,在start()函数中判断
  // always in loop t hread
  int nextConnId_; //下一个连接ID
  ConnectionMap connections_; //连接列表,实际是TcpConnection指针列表

方法

1. TcpServer:构造函数,初始化数据, 设置acceptor新连接到来的回调(这个回调是核心)

//需要传参EventLoop*,InetAddress&,服务名称字符串和枚举option
//各变量的初始化,新建Acceptor和EventLoopThreadPool,设置Acceptor的连接回调函数为newConnection
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)
{
  acceptor_->setNewConnectionCallback(
      std::bind(&TcpServer::newConnection, this, _1, _2));
}

2.newConnection: 该函数是acceptor新连接到来的回调(重要)

  1. 从IO线程池轮询获取一个loop;
  2.  给新连接一个名字;
  3. 创建新连接并在TcpServer连接容器中保存一份,防止智能指针引用计数为0会析构。
  4. 设置几个回调函数(这个其实是用户设置的)
  5.  最后的runInLoop,调用connectEstablished. 其实就是在loop中关注当前tcp连接的读事件
// 该函数作为acceptor_的新连接建立回调函数
// 新建一个TcpConnectionPtr连接对象,设置一些回调函数,最后运行TcpConnection::connectEstablished函数,这个函数就是把通道加入到Poller进行关注
void TcpServer::newConnection(int sockfd, const InetAddress& peerAddr)
{
  loop_->assertInLoopThread();
  EventLoop* ioLoop = threadPool_->getNextLoop(); //为新连接分配IO线程
  char buf[64];
  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));
  // 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));
  connections_[connName] = conn;
  conn->setConnectionCallback(connectionCallback_);
  conn->setMessageCallback(messageCallback_);
  conn->setWriteCompleteCallback(writeCompleteCallback_);
  conn->setCloseCallback(
      std::bind(&TcpServer::removeConnection, this, _1)); // FIXME: unsafe
  /*
      连接建立后,放到属于此tcpconnect的eventloop中执行TcpConnection::connectEstablished函数。
      connectEstablished函数中,监听此tcp连接的可读事件。并通知用户的onConnection
  */
  
  ioLoop->runInLoop(std::bind(&TcpConnection::connectEstablished, conn));  

}

3. removeConnection:注册给每一个TcpConnection的连接关闭的回调。

  1.  删除TcpServer的连接容器中保存的当前连接。
  2. 在TcpConnection所属的IO线程调用connectDestroyed,取消所有关注事件,并在poller中删除当前TcpConnection的channel。
//连接的关闭回调函数,调用removeConnectionInLoop
void TcpServer::removeConnection(const TcpConnectionPtr& conn)
{
  // FIXME: unsafe
  loop_->runInLoop(std::bind(&TcpServer::removeConnectionInLoop, this, conn));
}

// 从连接列表中移除这个连接,获得该连接的loop事件.把TcpConnection的connectDestroyed()函数加入到loop事件中
void TcpServer::removeConnectionInLoop(const TcpConnectionPtr& conn)
{
  loop_->assertInLoopThread();
  LOG_INFO << "TcpServer::removeConnectionInLoop [" << name_
           << "] - connection " << conn->name();
  size_t n = connections_.erase(conn->name());
  (void)n;
  assert(n == 1);
  EventLoop* ioLoop = conn->getLoop();
  ioLoop->queueInLoop(
      std::bind(&TcpConnection::connectDestroyed, conn));
}

4. setThreadNum:设置IO线程数量


//调用threadPool_->setThreadNum()设置线程池的线程数量
void TcpServer::setThreadNum(int numThreads)
{
  assert(0 <= numThreads);
  threadPool_->setThreadNum(numThreads);
}

5. start 

  1.   服务开启,主线程创建IO线程池中所有线程并收集所有线程的EventLoop*;
  2.  listen: acceptor开始监听,并往事件循环注册可读事件
// 该函数可以多次调用,可以跨线程调用
// 线程池start(),开启acceptor_的listen()函数,get_pointer()是muduo的获得智能指针的裸指针的函数
void TcpServer::start()
{
  if (started_.getAndSet(1) == 0)
  {
    //服务开启,主线程创建所有线程并收集所有线程的EventLoop*;
    threadPool_->start(threadInitCallback_);

    assert(!acceptor_->listening());
    //主循环中监听socket开始监听。
    loop_->runInLoop(
        std::bind(&Acceptor::listen, get_pointer(acceptor_)));
  }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

YanWenCheng_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值