【muduo网络库学习】之基本的TCP Server工作机制

在《linux多线程服务端编程》的8.5节讲述了基于eventloop,channel,acceptor等底层的类构建TCP server的过程,以及TcpConnection的初步实现。这块的程序被各式各样的回调函数所充斥着,可读性不是太好。现在把相关程序的流程记录一下,方便以后的学习。

首先还是看应用程序(使用tcpserver类的程序):

#include "TcpServer.h"
#include "EventLoop.h"
#include "InetAddress.h"
#include <stdio.h>

void onConnection(const muduo::TcpConnectionPtr& conn)
{
  if (conn->connected())
  {
    printf("onConnection(): new connection [%s] from %s\n",
           conn->name().c_str(),
           conn->peerAddress().toHostPort().c_str());
  }
  else
  {
    printf("onConnection(): connection [%s] is down\n",
           conn->name().c_str());
  }
}

void onMessage(const muduo::TcpConnectionPtr& conn,
               muduo::Buffer* buf,
               muduo::Timestamp receiveTime)
{
  printf("onMessage(): received %zd bytes from connection [%s] at %s\n",
         buf->readableBytes(),
         conn->name().c_str(),
         receiveTime.toFormattedString().c_str());

  printf("onMessage(): [%s]\n", buf->retrieveAsString().c_str());
}

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类构建了一个简易的 tcp 服务器,然后写了两个简单的回调函数onConnection, onMessage。当新接收一个Tcp 连接时候,server会调用onconnection,而每次收到数据时server则会调用onmessage来完成业务逻辑。下面具体来分析tcp server处理请求的流程。

首先是构建一个server对象

muduo::TcpServer server(&loop, listenAddr);
loop 和 listenAddr被用于构造server对象,tcpserver的构造函数如下:

TcpServer::TcpServer(EventLoop* loop, const InetAddress& listenAddr)
  : loop_(CHECK_NOTNULL(loop)),
    name_(listenAddr.toHostPort()),
    acceptor_(new Acceptor(loop, listenAddr)),
    started_(false),
    nextConnId_(1)
{
  acceptor_->setNewConnectionCallback(
      boost::bind(&TcpServer::newConnection, this, _1, _2));
}

如上所示,构造函数中会创建一个acceptor_对象来负责TCP请求的连接,Acceptor类自身的构造函数中会完成相应的socket准备工作,并且设定用于接收请求的channel回调函数 Acceptor::handleRead。

在Acceptor::handleRead的实现中,首先是调用accept()系统调用来接收连接,然后就会调用acceptor_->setNewConnectionCallback()方法,从而对应到tcpserver::newconnection中。


接下来是传递两个自定义回调函数onConnection,onMessage的句柄:

  server.setConnectionCallback(onConnection);
  server.setMessageCallback(onMessage);
这两个自定义的回调函数会在Tcpserver::newconnection中被使用,这个方法的源码:

void TcpServer::newConnection(int sockfd, const InetAddress& peerAddr)
{
  loop_->assertInLoopThread();
  char buf[32];
  snprintf(buf, sizeof buf, "#%d", nextConnId_);
  ++nextConnId_;
  std::string connName = name_ + buf;

  LOG_INFO << "TcpServer::newConnection [" << name_
           << "] - new connection [" << connName
           << "] from " << peerAddr.toHostPort();
  InetAddress localAddr(sockets::getLocalAddr(sockfd));
  // FIXME poll with zero timeout to double confirm the new connection
  TcpConnectionPtr conn(
      new TcpConnection(loop_, connName, sockfd, localAddr, peerAddr));
  connections_[connName] = conn;
  conn->setConnectionCallback(connectionCallback_);
  conn->setMessageCallback(messageCallback_);
  conn->setCloseCallback(
      boost::bind(&TcpServer::removeConnection, this, _1));
  conn->connectEstablished();
}
在这个方法中会新创建一个TcpConnection对象,这个对象以后就负责管理这个刚刚创建好的tcp连接,而onConnection,onMessage这两个回调函数的句柄会被赋值给TcpConnection对象。最后在这个方法中会调用connectEstablished方法:

void TcpConnection::connectEstablished()
{
  loop_->assertInLoopThread();
  assert(state_ == kConnecting);
  setState(kConnected);
  channel_->enableReading();
  connectionCallback_(shared_from_this());
}
这个函数里面会调用channel_->enableReading(),从而将channel加入loop中,然后调用用户自定义的connectionCallback_。当然,这里的channel是在tcp_connection中新创建的chanel对象,并为其设置了新的回调函数方法,如下:

cpConnection::TcpConnection(EventLoop* loop,
                             const std::string& nameArg,
                             int sockfd,
                             const InetAddress& localAddr,
                             const InetAddress& peerAddr)
  : loop_(CHECK_NOTNULL(loop)),
    name_(nameArg),
    state_(kConnecting),
    socket_(new Socket(sockfd)),
    channel_(new Channel(loop, sockfd)),
    localAddr_(localAddr),
    peerAddr_(peerAddr)
{
  LOG_DEBUG << "TcpConnection::ctor[" <<  name_ << "] at " << this
            << " fd=" << sockfd;
  channel_->setReadCallback(
      boost::bind(&TcpConnection::handleRead, this, _1));
  channel_->setWriteCallback(
      boost::bind(&TcpConnection::handleWrite, this));
  channel_->setCloseCallback(
      boost::bind(&TcpConnection::handleClose, this));
  channel_->setErrorCallback(
      boost::bind(&TcpConnection::handleError, this));
}
接下来,loop会继续循环,此时TCP连接已经建立好了。下一步poll返回时,实际上就是要进行数据交互了,而完成数据交互的回调函数则是由TcpConnection定义的各种handle方法来实现。比如说tcpConnection::handleRead

void TcpConnection::handleRead(Timestamp receiveTime)
{
  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();
  }
}

这个回调函数会先调用read系统调用读取数据到bufer,当读取的数据非量非0时,下一步就会调用messageCallback_这个用户自定义的函数来完成业务逻辑了,

以上就是整个程序的执行流程,对client而言,只需要定义并注册几个回调函数。底层的机制会完成相应的socket处理,并且在相应的时候比如连接建立时,有数据到来时调用用户自定义的函数来完成业务逻辑。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值