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

来源:互联网 发布:js下一个兄弟节点 编辑:程序博客网 时间:2024/05/02 23:06

在《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
原创粉丝点击