muduo源码阅读(三):TcpServer

TcpServer

功能 :这是一个接口类,拥有一个管理监听套接字的类accptor,拥有一张具有多个管理连接套接字的TcpConnection类的映射表。
它只是对这两个类进行管理,会设置它们的一些回调函数,监听端口,等,负责acceptor和TcpConnection两个类与用户交互的接口,而具体的调用实现还是由那两个具体的类去实现。比如连接到来时的调用函数,用户设置为onConnection,则接口TcpServer会将其设置为,TcpConnection类的connectioncallback函数,然后在建立函数的newconnection中调用。而将建立新的TcpConnection的TcpServer::NewConnection函数设置为acceptor的
类图

muduo源码阅读(三):TcpServer_第1张图片

从测试代码读起

先上一段测试代码

int main()
{
  printf("main(): pid = %d\n", getpid());

  muduo::InetAddress listenAddr(9981);
  muduo::EventLoop loop;

  muduo::TcpServer server(&loop, listenAddr);
  server.setConnectionCallback(onConnection);
  server.setMessageCallback(onMessage);
  server.start();

  loop.loop();
}

这段测试代码中,我们构造了一个TcpServer对象,然后调用了两个设置回调函数的成员函数,然后调用一个start函数。也就是说,我们使用TcpServer的时候,只要提供监听的端口号,注册连接时的回调函数,注册消息到来时的回调函数,即可。

设置端口号背后的故事

我们为 InetAddress 提供了端口号,那么muduo为我们做了什么呢?

explicit InetAddress(uint16_t port = 0, bool loopbackOnly = false, bool ipv6 = false);

InetAddress::InetAddress(uint16_t port, bool loopbackOnly, bool ipv6)
{
    memZero(&addr_, sizeof addr_);
    addr_.sin_family = AF_INET;
    in_addr_t ip = loopbackOnly ? kInaddrLoopback : kInaddrAny;
    addr_.sin_addr.s_addr = sockets::hostToNetwork32(ip);
    addr_.sin_port = sockets::hostToNetwork16(port);

}

这里对构造函数做了些删减,只留下了比较主干的部分,
在 InetAddress 的构造函数中,会创建一个 sockaddr_in_ 结构体对象,
假如我们只提供端口号,那么muduo会默认为ipv4,然后默认监听本机所有ip,初始化addr_成员,在初始化的时候,还用了sockets的一些转换函数,这些工作由muduo为我们做,可以为我们节省大量的时间和重复的工作量。
这样我们就完成了要监听的ip地址和端口号。

接着关注,TcpServer对象,
在TcpServer的构造函数:

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));//把newconnection设为了acceptor的回调函数
}

可以看到,该TcpServer有着所属的eventloop,监听的端口号,自己的名字,这都是一对一的属性。
同时这里创建了一个accptor_对象,这个对象会为我们完成一部分重要而规范化的工作。

acceptor介绍

功能 :调用accept建立新的TCP连接。每个accptor负责一个监听套接字,一旦有连接请求到来,会调用accept函数建立一个TcpConnection类,管理连接套接字。
类图muduo源码阅读(三):TcpServer_第2张图片
Acceptor的构造函数,调用socket完成了套接字的创建,调用bind绑定了套接字与服务器addr_结构体,也就是在TcpServer中创建的对象。

Acceptor::Acceptor(EventLoop* loop, const InetAddress& listenAddr, bool reuseport)
  : loop_(loop),
    acceptSocket_(sockets::createNonblockingOrDie(listenAddr.family())),
    acceptChannel_(loop, acceptSocket_.fd()),//初始化channel对应的文件描述符
    listenning_(false),
    idleFd_(::open("/dev/null", O_RDONLY | O_CLOEXEC))
{
  assert(idleFd_ >= 0);
  acceptSocket_.setReuseAddr(true);
  acceptSocket_.setReusePort(reuseport);
  acceptSocket_.bindAddress(listenAddr);
  acceptChannel_.setReadCallback(
      std::bind(&Acceptor::handleRead, this));//设置channel的读事件回调函数
}

//创建socket的函数   SocketOps.cc
int sockets::createNonblockingOrDie(sa_family_t family)
{
  int sockfd = ::socket(family, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, IPPROTO_TCP);
  if (sockfd < 0)
  {
    LOG_SYSFATAL << "sockets::createNonblockingOrDie";
  }
  return sockfd;
}

// 绑定监听地址 在
acceptSocket_.bindAddress(listenAddr);

//调用listen,转为监听状态:在TcpServer::start()中调用
void Acceptor::listen()
{
  loop_->assertInLoopThread();
  listenning_ = true;
  acceptSocket_.listen();
  acceptChannel_.enableReading();
}

因为我们的eventloop机制是通过channel来管理事件的,一个channel对应一个fd,这里的监听socketfd会对应一个channel,这里把该channel的读处理事件回调函数绑定为Acceptor::handleRead。也就是监听套接字可读,那就是发生了TCP连接事件,因此,我们应该想到这个时候是该调用accept来处理连接事件了。
Acceptor::handleRead就是通过调用accept(2)来建立了新的TCP连接

//channel读事件发生时会调用这个函数
void Acceptor::handleRead()
{
  loop_->assertInLoopThread();
  InetAddress peerAddr;
  //FIXME loop until no more
  int connfd = acceptSocket_.accept(&peerAddr);//调用accept建立连接,得到客户端fd
  if (connfd >= 0)//如果连接建立是正确的
  {
    // string hostport = peerAddr.toIpPort();
    // LOG_TRACE << "Accepts of " << hostport;
    if (newConnectionCallback_)
    {
      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);
    }
  }
}

建立连接以后,调用 newConnectionCallback_ 这个函数在TcpServer的构造函数中,被传入的是TcpServer::newConnection也就是后续创建Tcpconnection 的函数

  acceptor_->setNewConnectionCallback(
      std::bind(&TcpServer::newConnection, this, _1, _2));

现在用accept得到了连接套接字,连接套接字得到以后,我们就需要建立一个TcpConnection来维持两者的通信。


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对象
  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
  ioLoop->runInLoop(std::bind(&TcpConnection::connectEstablished, conn));
}

调用Tcpconnection的构造函数的时候,背后发生了什么呢?

先看一下构造函数代码:


TcpConnection::TcpConnection(EventLoop* loop,
                             const string& nameArg,
                             int sockfd,
                             const InetAddress& localAddr,
                             const InetAddress& peerAddr)
  : loop_(CHECK_NOTNULL(loop)),
    name_(nameArg),
    state_(kConnecting),
    reading_(true),
    socket_(new Socket(sockfd)),
    channel_(new Channel(loop, sockfd)),
    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);
}

构造函数:
socket_(new Socket(sockfd)),
channel_(new Channel(loop, sockfd)),
创建了一个socket作为连接套接字,创建一个channel负责该连接套接字
然后设置一些channel对应的处理回调函数
先分析两个相对重要的回调函数
第一个是处理读取的函数

void TcpConnection::handleRead(Timestamp receiveTime)
{
  loop_->assertInLoopThread();
  int savedErrno = 0;
  ssize_t n = inputBuffer_.readFd(channel_->fd(), &savedErrno);  //调用inputBuffer_的read函数
  if (n > 0)
  {
    messageCallback_(shared_from_this(), &inputBuffer_, receiveTime);//调用我们注册的消息到来时的函数
  }
  else if (n == 0)
  {
    handleClose();
  }
  else
  {
    errno = savedErrno;
    LOG_SYSERR << "TcpConnection::handleRead";
    handleError();
  }
}

这个回调函数主要是两件事:调用InputBuffer的读函数,调用我们注册的消息到来时的函数onMessage
处理回调写函数
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();
        }
      }
    }

你可能感兴趣的:(muduo)