【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
- 【muduo网络库学习】之基本的TCP Server工作机制
- muduo网络库学习之muduo_http 库涉及到的类
- muduo网络库学习之muduo_inspect 库涉及到的类
- muduo网络库学习之muduo_inspect 库涉及到的类
- muduo源码分析之实现TCP网络库(连接的接收和关闭)
- C++ Muduo网络库基本流程跟踪,学习下
- muduo网络库学习(五)服务器监听类Acceptor及Tcp连接TcpConnection的建立与关闭
- muduo网络库学习之EventLoop(七):TcpClient、Connector
- 【muduo网络库学习】之Acceptor类分析
- muduo库的学习6---Buffer的基本设计
- muduo库的学习1---socket基本设计---总论
- muduo库的学习3---loop基本设计---总论
- 学习开源网络库muduo的一点心得
- muduo网络库学习笔记(5):线程池的实现
- muduo网络库学习笔记(6):单例类(线程安全的)
- muduo网络库学习笔记(10):定时器的实现
- muduo网络库学习(三)定时器TimerQueue的设计
- 【网络编程】深入理解TCP的工作机制详解
- xmlPull解析网络数据(xml文件)
- spring mvc @PathVariable / 带斜杠方式获取
- python下配置opencv
- Java流(Stream)、文件(File)和IO
- 小玩文件(1)
- 【muduo网络库学习】之基本的TCP Server工作机制
- dropList下拉框,可微调控制在ScrollView上的位置,通过设置下拉框的X、Y值为ScrollView的偏移量即可
- file类型的输入框,样式不可修改的解决方法
- windows 下的GSL配置(转载)
- Pre设计模式相关docs
- 配置mongodb 复制集3.2
- Android中Fragment
- ScrollView滑动的时候去除边上的阴影
- Android SQLiteDatabase帮助类SQLiteOpenHelper的使用