Muduo TcpConnection 生命周期

TcpConnection对象生命周期

class TcpServer : noncopyable
{
 public:
  typedef std::function<void(EventLoop*)> ThreadInitCallback;
  enum Option
  {
    kNoReusePort,	//不复用端口
    kReusePort,		//复用端口
  };

  //TcpServer(EventLoop* loop, const InetAddress& listenAddr);
  TcpServer(EventLoop* loop,
            const InetAddress& listenAddr,		//绑定端口和主机地址,并且提供单独的输出函数(InetAddress类)
            const string& nameArg,
            Option option = kNoReusePort);
  ~TcpServer();  // force out-line dtor, for std::unique_ptr members.

  const string& ipPort() const { return ipPort_; }
  const string& name() const { return name_; }
  EventLoop* getLoop() const { return loop_; }

  /// Set the number of threads for handling input.
  ///
  /// Always accepts new connection in loop's thread.
  /// Must be called before @c start
  /// @param numThreads
  /// - 0 means all I/O in loop's thread, no thread will created.
  ///   this is the default value.
  /// - 1 means all I/O in another thread.
  /// - N means a thread pool with N threads, new connections
  ///   are assigned on a round-robin basis.
  void setThreadNum(int numThreads);
  void setThreadInitCallback(const ThreadInitCallback& cb)
  { threadInitCallback_ = cb; }
  /// valid after calling start()
  std::shared_ptr<EventLoopThreadPool> threadPool()
  { return threadPool_; }

  /// Starts the server if it's not listening.
  ///
  /// It's harmless to call it multiple times.
  /// Thread safe.
  void start();

  /// Set connection callback.
  /// Not thread safe.
  void setConnectionCallback(const ConnectionCallback& cb)
  { connectionCallback_ = cb; }

  /// Set message callback.
  /// Not thread safe.
  void setMessageCallback(const MessageCallback& cb)
  { messageCallback_ = cb; }

  /// Set write complete callback.
  /// Not thread safe.
  void setWriteCompleteCallback(const WriteCompleteCallback& cb)
  { writeCompleteCallback_ = cb; }

 private:
  /// Not thread safe, but in loop
  void newConnection(int sockfd, const InetAddress& peerAddr);
  /// Thread safe.
  void removeConnection(const TcpConnectionPtr& conn);
  /// Not thread safe, but in loop
  void removeConnectionInLoop(const TcpConnectionPtr& conn);

  typedef std::map<string, TcpConnectionPtr> ConnectionMap;

	//每个线程对应一个EventLoop是怎样做到的

  EventLoop* loop_;  // the acceptor loop  主线程中的loop
  const string ipPort_;
  const string name_;	//服务器名
  std::unique_ptr<Acceptor> acceptor_; // avoid revealing Acceptor
  std::shared_ptr<EventLoopThreadPool> threadPool_;		//开启多线程EventLoop
  ConnectionCallback connectionCallback_;	//默认打印日志
  MessageCallback messageCallback_;	//默认重置buffer
  WriteCompleteCallback writeCompleteCallback_;	//写回调
  ThreadInitCallback threadInitCallback_;	//开启IO线程回调默认为空
  AtomicInt32 started_;
  // always in loop thread
  int nextConnId_;
  ConnectionMap connections_;	//保存conn连接
};

构造函数

在该函数中我们可以看到acceptor_这个成员变量,该变量是主线程用于监听用户连接的

在函数中有一个Acceptor成员

TcpServer::TcpServer(EventLoop* loop,	
                     const InetAddress& listenAddr,
                     const string& nameArg,
                     Option option)
  : loop_(CHECK_NOTNULL(loop)),		//用户传入的loop,也是主线的loop
    ipPort_(listenAddr.toIpPort()),
    name_(nameArg),	 //标识和生成唯一键值
    acceptor_(new Acceptor(loop, listenAddr, option == kReusePort)),//acceptor中存在套接字,回调进行事件分发
    threadPool_(new EventLoopThreadPool(loop, name_)),//IO线程池 每一个元素为一个reactor
    connectionCallback_(defaultConnectionCallback),//默认为打印日志
    messageCallback_(defaultMessageCallback),//默认重置buffer
    nextConnId_(1)	//标识和生成唯一键值
{
  acceptor_->setNewConnectionCallback(	
      std::bind(&TcpServer::newConnection, this, _1, _2));
  //新连接到来的时候会回调newConnection 产生一个TcpConnection对象
}

成功连接后的回调函数

void TcpServer::newConnection(int sockfd, const InetAddress& peerAddr)//peerAddr 客端的地址与协议
{
  loop_->assertInLoopThread();	//是否为IO线程
  EventLoop* ioLoop = threadPool_->getNextLoop();	//获取一个EventLoop对象
  //事件分发机制 
  char buf[64];
  snprintf(buf, sizeof buf, "-%s#%d", ipPort_.c_str(), nextConnId_);//ip+地址+当前连接的序号
  ++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));	//创建一个TcpConnection
  connections_[connName] = conn;	//放入map 引用计数增加

  conn->setConnectionCallback(connectionCallback_);			//连接回调	    默认为打印日志(如果用户未设置)
  conn->setMessageCallback(messageCallback_);				//信息回调	默认重置缓冲区
  conn->setWriteCompleteCallback(writeCompleteCallback_);	//写操作完成时触发的回调 默认为空
  conn->setCloseCallback(
      std::bind(&TcpServer::removeConnection, this, _1)); // FIXME: unsafe	断开连接回调
      
  ioLoop->runInLoop(std::bind(&TcpConnection::connectEstablished, conn));	//加入loop事件循环 并延长conn的生命周期
}

这里我们主要讲下TcpConnection的生命周期(用户不持有的情况下)

typedef std::shared_ptr<TcpConnection> TcpConnectionPtr;

我们使用shared_ptr来维护TcpConnection,因为它的生命周期是很模糊的,此处以两个地方来讲述:

第一点

当Channel关注的事件发生后,在loop()循环中会把这些Channel捕获到并且依次回调它们关注事件的函数,我们知道

Channel的生命周期是由它的上层来管理的(这里单独指用户连接),也就是TcpConnection对象.如果此时Channel被放入待处理队列后TcpConnection对象被析构了,那么Channel也会被析构掉,此时程序就会core dump.

那么Muduo是怎么处理这个问题呢

void Channel::handleEvent(Timestamp receiveTime)
{
  std::shared_ptr<void> guard;	//在该函数执行完后,该指针指向的对象引用计数减少1
  if (tied_)	//判断是否有shared_ptr被接收,
  {
    guard = tie_.lock();	//提升为shared_ptr
    if (guard)
    {
      handleEventWithGuard(receiveTime);
    }
  }
  else	//没有shared_ptr,则直接执行
  {
    handleEventWithGuard(receiveTime);
  }
}

第一个if是用于判断这个Channel是否是TcpConnection对象的Chnnel 我们来讲明白这句话

先看下面这个函数

void Channel::tie(const std::shared_ptr<void>& obj)
{
  tie_ = obj;	
  tied_ = true;	//收到了TcpConnection传递的 shared_ptr
}

收到了TcpConnection传递的 shared_ptr ,那么我们什么时候传递的shared_ptr呢? 可以在newConnection()看到ioLoop->runInLoop(std::bind(&TcpConnection::connectEstablished, conn));

void TcpConnection::connectEstablished()
{
  loop_->assertInLoopThread();
  assert(state_ == kConnecting);
  setState(kConnected);
  channel_->tie(shared_from_this());//shared_from_this() 返回一个当前类的shared_ptr
  channel_->enableReading();

  connectionCallback_(shared_from_this());
}

好了,这里我们就知道了 tie_接收的就是 shared_from_this()返回指向 TcpConnection 的shared_ptr

void Channel::handleEvent(Timestamp receiveTime)
{
  std::shared_ptr<void> guard;	//在该函数执行完后,该指针指向的对象引用计数减少1
  if (tied_)	//判断是否有shared_ptr被接收,
  {
    guard = tie_.lock();	//提升为shared_ptr
    if (guard)
    {
      handleEventWithGuard(receiveTime);
    }
  }
  else	//没有shared_ptr,则直接执行
  {
    handleEventWithGuard(receiveTime);
  }
}

此时如果是TcpConnection的Channel被处理,那么它就会进入这个if,之后在通过 tie_.lock() 提升为shared_ptr(前提是这个weak_ptr观察的shared_ptr还是存在的),提升成功会增加 shared_ptr 的计数从而延长TcpConnection对象的生命周期,保证它能在执行回调时不会被析构

第二点

从TcpConnection的断开说起

void TcpServer::newConnection(int sockfd, const InetAddress& peerAddr)//peerAddr 客端的地址与协议
{
  loop_->assertInLoopThread();	//是否为IO线程
  EventLoop* ioLoop = threadPool_->getNextLoop();	//获取一个EventLoop对象
  //事件分发机制 
  char buf[64];
  snprintf(buf, sizeof buf, "-%s#%d", ipPort_.c_str(), nextConnId_);//ip+地址+当前连接的序号
  ++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));	//创建一个TcpConnection
  connections_[connName] = conn;	//放入map 引用计数增加

  conn->setConnectionCallback(connectionCallback_);			//连接回调	    默认为打印日志(如果用户未设置)
  conn->setMessageCallback(messageCallback_);				//信息回调	默认重置缓冲区
  conn->setWriteCompleteCallback(writeCompleteCallback_);	//写操作完成时触发的回调 默认为空
  conn->setCloseCallback(
      std::bind(&TcpServer::removeConnection, this, _1)); // FIXME: unsafe	断开连接回调
      
  ioLoop->runInLoop(std::bind(&TcpConnection::connectEstablished, conn));	//加入loop事件循环 并延长conn的生命周期
}

我们从TcpConnection的 CloseCallback()开始说,那么我们需要知道什么时候调用这个 CloseCallback(),答案在

TcpConnection::handleRead

void TcpConnection::handleRead(Timestamp receiveTime)
{
  loop_->assertInLoopThread();
  int savedErrno = 0;
  ssize_t n = inputBuffer_.readFd(channel_->fd(), &savedErrno);		//读取数据到inputBuffer中
  if (n > 0)
  {
    messageCallback_(shared_from_this(), &inputBuffer_, receiveTime);	//调用用户设置的回调,并且把刚刚读取到信息的inputBuffer一起传递
  }
  else if (n == 0)	//对端关闭
  {
    handleClose();	//此时Channel还是在执行handleEvent(),所以不能析构Channel
  }
  else
  {
    errno = savedErrno;
    LOG_SYSERR << "TcpConnection::handleRead";
    handleError();
  }
}


void TcpConnection::handleClose()
{
  loop_->assertInLoopThread();
  LOG_TRACE << "fd = " << channel_->fd() << " state = " << stateToString();
  assert(state_ == kConnected || state_ == kDisconnecting);
  // we don't close fd, leave it to dtor, so we can find leaks easily.
  setState(kDisconnected);	//设置当前conn的状态
  channel_->disableAll();	

  TcpConnectionPtr guardThis(shared_from_this());	//conn的计数 ++ 
  connectionCallback_(guardThis);
  // must be the last line
  closeCallback_(guardThis);	
}

closeCallback_(guardThis);被设置为
conn->setCloseCallback(std::bind(&TcpServer::removeConnection, this, _1));

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


void TcpServer::removeConnectionInLoop(const TcpConnectionPtr& conn)	
{
  loop_->assertInLoopThread();
  LOG_INFO << "TcpServer::removeConnectionInLoop [" << name_
           << "] - connection " << conn->name();
  size_t n = connections_.erase(conn->name());	//从map中删除 计数-1  
  (void)n;
  assert(n == 1);
  EventLoop* ioLoop = conn->getLoop();
  /*
	此处一定要用EventLoop::queueInLoop(),避免Channel对象被提前销毁
	这里用std::bind让TcpConnection的生命期长到调用connectDestroyed()的时刻
	使用std::bind得到一个boost::function对象,会把conn传递进去,引用计数会加1
  */	
  ioLoop->queueInLoop(
      std::bind(&TcpConnection::connectDestroyed, conn));	//传递 计数++
}


void TcpConnection::connectDestroyed()
{
  loop_->assertInLoopThread();
  if (state_ == kConnected)
  {
    setState(kDisconnected);
    channel_->disableAll();

    connectionCallback_(shared_from_this());
  }
  channel_->remove();	//移除channel

  //TcpConntion的生命周期在这个函数中才会结束(如果用户不持有TcpConnection对象)
}

此处一定要用EventLoop::queueInLoop(),避免Channel对象被提前销毁

为什么一定要用这个函数呢,因为runInLoop()会判断当前线程为IO线程就直接执行传入的cb了,那么connectDestroyed()就被调用,然后TcpConnection被析构,对应的Channel也被析构(如果此时的Channel在被处理队列中就会造成程序崩溃)

void EventLoop::runInLoop(Functor cb)
{
  if (isInLoopThread())		//如果当前是IO线程,那么同步执行任务回调
  {	
    cb();
  }
  else				//否则放入队列中
  {
    queueInLoop(std::move(cb));
  }
}


void EventLoop::queueInLoop(Functor cb)
{
  {
  MutexLockGuard lock(mutex_);		//暴露给了其他线程需要上锁
  pendingFunctors_.push_back(std::move(cb));		//加入队列
  }
	//此时只会执行进入这个临界区之前放入队列的任务,后续的任务等到下次循环再执行

  if (!isInLoopThread() || callingPendingFunctors_)
  {
    wakeup();	//往wakefd_写入数据,唤醒IO multiplexing
  }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值