muduo源码分析 TcpSerer 接收一个链接


前面说了链接前的准备工作,下面说一下链接的过程。
当一个链接发起的时候,EventLoop首先将产生的就绪事件的文件描述符取出,并且取出对应的该文件描述符对应的Channel,看一个Channel结构中记录了什么。

Channel 基本的成员变量

	//三个静态变量
  static const int kNoneEvent;	//没有事件
  static const int kReadEvent;	//读事件
  static const int kWriteEvent;	//写事件

  //事件循环loop 
  EventLoop* loop_;
  //文件描述符
  const int  fd_;
  //监听的事件
  int        events_;
  //就绪的事件
  int        revents_; // it's the received event types of epoll or poll
  //当前文件描述符在 文件描述符数组中的位置
  int        index_; // used by Poller.
  bool       logHup_;
  //注册的回调函数
  //读事件的回调函数
  ReadEventCallback readCallback_;
  //写事件的回调函数
  EventCallback writeCallback_;
  //关闭事件的回调函数
  EventCallback closeCallback_;
  //错误事件的回调删数
  EventCallback errorCallback_;

Channel中有该文件描述符,监听的文件描述符的事件,该文件描述符上现在就绪的事件,还有各个事件所对应的回调函数,Channel回调函数中包含了对文件描述符处理的的一切。看一下Channel 的事件分发函数。

handleEvent事件分发

//该函数最终调用handleEventWithGuard 函数
//receiveTime 事件就绪的事件,当有事件发生的时候,需要更新定时器
void Channel::handleEvent(Timestamp receiveTime)
{
  std::shared_ptr<void> guard;
  if (tied_)
  {
    guard = tie_.lock();
    if (guard)
    {
      handleEventWithGuard(receiveTime);
    }
  }
  else
  {
    handleEventWithGuard(receiveTime);
  }
}

//具体处理到达的事件,通过回调函数进行事件的分发
void Channel::handleEventWithGuard(Timestamp receiveTime)
{
  eventHandling_ = true;
  //记录日志
  LOG_TRACE << reventsToString();
  //下面根据事件的不同,调用相应的回调函数,
  //这里我们看一下读事件的回调函数
  if ((revents_ & POLLHUP) && !(revents_ & POLLIN))
  {
    if (logHup_)
    {
      LOG_WARN << "fd = " << fd_ << " Channel::handle_event() POLLHUP";
    }
    if (closeCallback_) closeCallback_();
  }

  if (revents_ & POLLNVAL)
  {
    LOG_WARN << "fd = " << fd_ << " Channel::handle_event() POLLNVAL";
  }

  if (revents_ & (POLLERR | POLLNVAL))
  {
    if (errorCallback_) errorCallback_();
  }
  //当有读事件到达的时候,调用注册的readCallBack函数。
  //在Acceptor 中我们知道,注册回调函数为Acceptor::handleRead
  //下面直接看一下Acceptor::handleRead 函数
  if (revents_ & (POLLIN | POLLPRI | POLLRDHUP))
  {
    if (readCallback_) readCallback_(receiveTime);
  }
  if (revents_ & POLLOUT)
  {
    if (writeCallback_) writeCallback_();
  }
  eventHandling_ = false;
}

Acceptor::handleRead 处理读事件

void Acceptor::handleRead()
{
  loop_->assertInLoopThread();
  InetAddress peerAddr;
  //FIXME loop until no more
  //有新连接到达时调用accept接收一个新的链接
  int connfd = acceptSocket_.accept(&peerAddr);
  if (connfd >= 0)
  {
    // string hostport = peerAddr.toIpPort();
    // LOG_TRACE << "Accepts of " << hostport;
    //如果注册了回调函数,则调用回调函数,将客户端的地址和新产生的文件描述符传入
    //在前面TcpServer构造函数中,我们看到这里注册了回调函数 TcpServer::newConnection
    if (newConnectionCallback_)
    {
    //回调tcpserver 的newConnection
      newConnectionCallback_(connfd, peerAddr);
    }
    //如果没有注册回调函数,直接关闭文件描述符
    else
    {
      sockets::close(connfd);
    }
  }
  else
  {
    LOG_SYSERR << "in Acceptor::handleRead";
    // Read the section named "The special problem of
    // accept()ing when you can't" in libev's doc.
    // By Marc Lehmann, author of libev.
    if (errno == EMFILE)
    {
      ::close(idleFd_);
      idleFd_ = ::accept(acceptSocket_.fd(), NULL, NULL);
      ::close(idleFd_);
      idleFd_ = ::open("/dev/null", O_RDONLY | O_CLOEXEC);
    }
  }
}

TcpServer::newConnection 处理新的连接

//该函数的功能
//对新产生的文件描述符加入的事件循环中,并产生一个新的Channel 对象管理这个新产生
//的文件描述符,设置一系列的对应的回调函数,写入日志
void TcpServer::newConnection(int sockfd, const InetAddress& peerAddr)
{
  loop_->assertInLoopThread();
  EventLoop* ioLoop = threadPool_->getNextLoop();
  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
  //在TcpConnection 有一个Channel对象,用于对新产生的文件描述符产生的事件进行分发
  //一个文件描述符对应一个Channel 对象
  TcpConnectionPtr conn(new TcpConnection(ioLoop,
                                          connName,
                                          sockfd,
                                          localAddr,
                                          peerAddr));
  //将对象放到map 表中,这里用来管理所有的连接对象
  connections_[connName] = conn;
  //设置回调函数
  conn->setConnectionCallback(connectionCallback_);
  conn->setMessageCallback(messageCallback_);
  conn->setWriteCompleteCallback(writeCompleteCallback_);
  conn->setCloseCallback(
      std::bind(&TcpServer::removeConnection, this, _1)); // FIXME: unsafe
  //这里将新产生的文件描述符添加到事件循环中
  //监听当前的conn对象
  ioLoop->runInLoop(std::bind(&TcpConnection::connectEstablished, conn));
}

到此为止,已经可以与一个客户端建立连接了,并且可以监听连接后的新的文件描述符,与客户端进行通信。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值