muduo网络库——TcpServer、Acceptor和TcpConnection

本文主要介绍的是muduo网络库对于tcp通信的相关处理及源码分析,关于tcp相关知识,可以参考:https://blog.csdn.net/www_dong/category_10706734.html

模型

img

tcp相关类说明:

TcpConnection: TCP连接管理

Acceptor: 接收连接

Connector: 发起连接

TcpServer: 管理Acceptor获取的连接

实现流程:

1)TcpServer构造时会创建Acceptor对象并设置新连接回调;

2)Acceptor接收Channel类的handleRead回调,在handleRead函数中调用socket的accept的接口接收新连接,接收成功后回调至TcpServer;

3)TcpServer成功接收回调后创建TcpConnection对象(通过std::shared_d智能指针管理),创建读、写、关闭等回调,并将连接加入到EventLoop中;

4)TcpConnection类做连接成功处理(设置连接状态、Channel绑定当前连接、读使能等);

源码分析

TcpServer

TcpServer类管理TcpConnection,供用户直接使用,生命周期由用户控制。用户只需要设置好callback,然后调用start()即可。

构造函数

主要完成以下几项任务:

1)创建Acceptor对象,用于接收新连接;

2)创建线程池EventLoopThreadPool的实例;

3)接收并保存回调,当有消息时进行回调;

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));
}

析构函数

移除所有连接。

TcpServer::~TcpServer()
{
  loop_->assertInLoopThread();
  LOG_TRACE << "TcpServer::~TcpServer [" << name_ << "] destructing";

  for (auto& item : connections_)
  {
    TcpConnectionPtr conn(item.second);
    item.second.reset();
    conn->getLoop()->runInLoop(
      std::bind(&TcpConnection::connectDestroyed, conn));
  }
}

newConnection

1)接收Acceptor回调;

2)创建TcpConnection;

3)将当前连接加入事件循环;

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
  TcpConnectionPtr conn(new TcpConnection(ioLoop,
                                          connName,
                                          sockfd,
                                          localAddr,
                                          peerAddr));  // 创建TcpConnection对象
  connections_[connName] = conn;
  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)); // 将连接加入事件循环
}

start(供用户调用)

1)启动线程池;

2)启动监听;

void TcpServer::start()
{
  if (started_.getAndSet(1) == 0)
  {
    threadPool_->start(threadInitCallback_);  // 线程池启动

    assert(!acceptor_->listening());
    loop_->runInLoop(
        std::bind(&Acceptor::listen, get_pointer(acceptor_)));   // 启动监听
  }
}

get_pointer

用于获取智能指针维护的原始指针。

template<typename T>
inline T* get_pointer(const std::shared_ptr<T>& ptr)
{
  return ptr.get();
}

Acceptor

主要功能如下:

1)启动监听;

2)处理Channel连接回调;

构造函数

1)初始化socket;

2)设置socket监听;

3)设置Channel类的handleRead回调,处理新连接;

Acceptor::Acceptor(EventLoop* loop, const InetAddress& listenAddr, bool reuseport)
  : loop_(loop),
    acceptSocket_(sockets::createNonblockingOrDie(listenAddr.family())),
    acceptChannel_(loop, acceptSocket_.fd()),
    listening_(false),
    idleFd_(::open("/dev/null", O_RDONLY | O_CLOEXEC)) // 空闲fd指向文件"/dev/null",用来解决服务器fd资源耗尽问题
{
  assert(idleFd_ >= 0);
  acceptSocket_.setReuseAddr(true);
  acceptSocket_.setReusePort(reuseport);
  acceptSocket_.bindAddress(listenAddr);
  acceptChannel_.setReadCallback(
      std::bind(&Acceptor::handleRead, this));
}

析构函数

Acceptor::~Acceptor()
{
  acceptChannel_.disableAll();
  acceptChannel_.remove();
  ::close(idleFd_);
}

idleFd_

Acceptor用了这样一种技术:
先申请一个空闲的fd(idleFd_),等到发生由于fd资源不够用时,就把这个备用fd暂时用于accept接收连接,然后再马上关闭,以防止不断产生可读事件(连接请求),从而回调相同的失败代码。及早建立连接后并关闭连接,让程序不会频繁响应同一个连接请求。

# 相关代码如下
if (errno == EMFILE)
{
	::close(idleFd_);
	idleFd_ = ::accept(acceptSocket_.fd(), NULL, NULL);
	::close(idleFd_);
	idleFd_ = ::open("/dev/null", O_RDONLY | O_CLOEXEC);
}

listen

监听本地sock fd, 使能监听Channel读事件。

void Acceptor::listen()
{
  loop_->assertInLoopThread();
  listening_ = true;
  acceptSocket_.listen();          // 开始监听
  acceptChannel_.enableReading();  // Channel使能读事件
}

TcpConnection

TcpConnection使用Channel获得socket上IO事件,它会自己处理writable事件,把可读事件通过MessageCallback传递给客户。

构造函数

主要功能如下:

1)创建Channel对象;

2)设置Channel读、写、关闭和错误事件回调;

3)调用Socket类创建socket_对象,sockfd由TcpServer传入;

TcpConnection::TcpConnection(EventLoop* loop,
                             const string& nameArg,
                             int sockfd,
                             const InetAddress& localAddr,
                             const InetAddress& peerAddr)
  : loop_(CHECK_NOTNULL(loop)),
    name_(nameArg),
    state_(kConnecting),            // 设置连接状态为kConnecting
    reading_(true),
    socket_(new Socket(sockfd)),    // 创建socket
    channel_(new Channel(loop, sockfd)),  // 创建channel_
    localAddr_(localAddr),
    peerAddr_(peerAddr),
    highWaterMark_(64*1024*1024)
{
  channel_->setReadCallback(
      std::bind(&TcpConnection::handleRead, this, _1));  
  channel_->setWriteCallback(
      std::bind(&TcpConnection::handleWrite, this));
  channel_->setCloseCallback(
      std::bind(&TcpConnection::handleClose, this));
  channel_->setErrorCallback(
      std::bind(&TcpConnection::handleError, this));
  LOG_DEBUG << "TcpConnection::ctor[" <<  name_ << "] at " << this
            << " fd=" << sockfd;
  socket_->setKeepAlive(true);
}

消息发送

sendInLoop是loop线程排队发送消息的核心函数,主要是向对端发送一次数据,如果发送完一次,就进行一次回调;如果待发送数据超高水位,就进行高水位回调;如果发生错误,就进行错误回调。

void TcpConnection::sendInLoop(const void* data, size_t len)
{
  loop_->assertInLoopThread();
  ssize_t nwrote = 0;
  size_t remaining = len;
  bool faultError = false;
  if (state_ == kDisconnected)
  {
    LOG_WARN << "disconnected, give up writing";
    return;
  }
  
  // if no thing in output queue, try writing directly
  if (!channel_->isWriting() && outputBuffer_.readableBytes() == 0)  // 如果通道没有使能监听写事件, 并且outputBuffer 没有待发送数据, 就直接通过socket写
  {
    nwrote = sockets::write(channel_->fd(), data, len);
    if (nwrote >= 0)
    {
      remaining = len - nwrote;
      if (remaining == 0 && writeCompleteCallback_)   // 数据已写完
      {
        loop_->queueInLoop(std::bind(writeCompleteCallback_, shared_from_this()));
      }
    }
    else // nwrote < 0
    {
      nwrote = 0;
      if (errno != EWOULDBLOCK)
      {
        LOG_SYSERR << "TcpConnection::sendInLoop";
        if (errno == EPIPE || errno == ECONNRESET) // FIXME: any others?
        {
          faultError = true;
        }
      }
    }
  }

  // 处理剩余待发送数据
  assert(remaining <= len);
  if (!faultError && remaining > 0) //没有故障, 并且还有待发送数据, 可能是发送太快, 对方来不及接收
  {
    size_t oldLen = outputBuffer_.readableBytes();
    if (oldLen + remaining >= highWaterMark_   // // Buffer及当前要发送的数据量之和 超 高水位(highWaterMark)
        && oldLen < highWaterMark_   // 单独的Buffer中待发送数据量 未超 高水位
        && highWaterMarkCallback_)
    {
      loop_->queueInLoop(std::bind(highWaterMarkCallback_, shared_from_this(), oldLen + remaining));
    }
    outputBuffer_.append(static_cast<const char*>(data)+nwrote, remaining);
    if (!channel_->isWriting())
    {
      channel_->enableWriting();
    }
  }
}

handleRead

inputBuffer_中读取到数据,则执行上层消息回调。

void TcpConnection::handleRead(Timestamp receiveTime)
{
  loop_->assertInLoopThread();
  int savedErrno = 0;
  ssize_t n = inputBuffer_.readFd(channel_->fd(), &savedErrno);
  if (n > 0)
  {
    messageCallback_(shared_from_this(), &inputBuffer_, receiveTime);
  }
  else if (n == 0)
  {
    handleClose();
  }
  else
  {
    errno = savedErrno;
    LOG_SYSERR << "TcpConnection::handleRead";
    handleError();
  }
}

handleWrite

void TcpConnection::handleWrite()
{
  loop_->assertInLoopThread();
  if (channel_->isWriting())
  {
    ssize_t n = sockets::write(channel_->fd(),
                               outputBuffer_.peek(),
                               outputBuffer_.readableBytes());
    if (n > 0)
    {
      outputBuffer_.retrieve(n);
      if (outputBuffer_.readableBytes() == 0)
      {
        channel_->disableWriting();
        if (writeCompleteCallback_)
        {
          loop_->queueInLoop(std::bind(writeCompleteCallback_, shared_from_this()));  // 执行写完成回调
        }
        if (state_ == kDisconnecting)  // 当前未连接 停止事项循环
        {
          shutdownInLoop();
        }
      }
    }
    else
    {
      LOG_SYSERR << "TcpConnection::handleWrite";
      // if (state_ == kDisconnecting)
      // {
      //   shutdownInLoop();
      // }
    }
  }
  else
  {
    LOG_TRACE << "Connection fd = " << channel_->fd()
              << " is down, no more writing";
  }
}

handleClose

主要处理以下几项任务:

1)设置状态为未连接;

2)通道不使能;

3)关闭回调上层;

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);
  channel_->disableAll();

  TcpConnectionPtr guardThis(shared_from_this());
  connectionCallback_(guardThis);
  // must be the last line
  closeCallback_(guardThis);
}

Channel断开连接

Channel断开连接流程:

1)Poller::poll()检测到Channel事件就绪;

2)EventLoop检测事件;

3)Channel::handleEvent()处理事件;

4)Channel::closeCallback_回调给TcpConnection事件;

5)TcpConnection::handleClose()处理事件关闭;

connectDestroyed(资源销毁)

连接断开的调用流程如下:

TcpServer::removeConnection <--- TcpServer::removeConnectionInLoop <--- TcpConnection::connectDestroyed

TcpServer中调用removeConnection移除连接。

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());
  (void)n;
  assert(n == 1);
  EventLoop* ioLoop = conn->getLoop();  // 获取当前事件循环
  ioLoop->queueInLoop(
      std::bind(&TcpConnection::connectDestroyed, conn));  // 确保是当前循环, 执行TcpConnection中的connectDestroyed
}

connectDestroyed处理如下:

注意:只有状态为kConnected的连接时,才可以采取断开连接动作。

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

    connectionCallback_(shared_from_this());
  }
  channel_->remove();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值