我报名参加金石计划1期挑战——瓜分10万奖池,这是我的第1篇文章,点击查看活动详情
muduo网络库
muduo是一个基于非阻塞IO和事件驱动的C++网络库,采用Reactor模式和one loop per thread + thread pool线程模型。
类功能介绍
Channel.*
,Poller.*
,EventLoop.*
三类为一体,在底层负责事件循环。EventLoop包含Channel,Poller,EventLoop负责轮询访问Poller,得到激活Channel列表,使Channel自己根据自身情况调用相应回调。
Thread.*
,EventLoopThread.*
,EventLoopThreadPool.*
将线程与EventLoop绑定在一起,实现one loop per thread + thread pool线程模型。
TcpServer.*
,Acceptor.*
,TcpConnection.*
为上层Reactor模式的实现。TcpServer面向用户,可由用户自定义回调方法以及启动服务器。Acceptor也看做分发器,当有新连接到来时,用来将连接转发给子线程处理。TcpConnection在子线程处理被分来的连接的具体事件。
执行流程
- 用户创建TcpServer对象调用
start()
开启服务器,启动线程池,启动一个baseLoop(主线程),由runInLoop()
处理Acceptor(Reactor)的listen()
方法,进行监听操作; - 一旦有客户端请求连接,Accetor调用TcpServer设置的newConnectionCallback_ ,将请求分发给subLoop(子线程),即从线程池中取出,然后将连接丢给子线程,由TcpConnection处理,此后该连接的所有操作都由该子线程独自处理,与其他线程无关。
- TcpConnection将读,写,关闭,错误操作与回调填充至绑定的Channel,然后调用
runInLoop()
和queueInLoop()
由底层事件循环处理。 - 底层事件循环中,主线程称为baseLoop,只执行监听操作与用户回调。而子线程具体执行操作由Channel具体返回的事件所执行。由图所示,蓝色部分由三类组成,EventLoop,Channel和Poller。每个EventLoop分别与各自的线程绑定,同时将Poller和Channel连接起来,调用loop()方法将Channel的结果送入Poller执行,将Poller返回的结果各自送入Channel执行。
核心模块 |
---|
Socket |
Buffer |
Channel |
Poller与Epoller |
EventLoop与EventLoopThread |
EventLoopThreadPool |
Acceptor |
TcpConnection |
TcpServer |
TcpServer类
TcpServer类面向用户,用户在编码服务器时,对TcpServer类进行创建和设置。
TcpServer类包含如下数据成员:
private:
using ConnectionMap = std::unordered_map<std::string, TcpConnectionPtr>;
// 首先TcpServer含有一个baseLoop,此Loop绑定主线程,只负责监听操作。
EventLoop *loop_; // baseloop
// 服务器信息
const std::string ipPort_;
const std::string name_;
// 含有acceptor,类似于分发起,封装监听操作
std::unique_ptr<Acceptor> acceptor_;
// 服务器含有的线程池
std::shared_ptr<EventLoopThreadPool> threadPool_;
// 连接回调,读写回调,由用户编写
ConnectionCallback connectionCallback_;
MessageCallback messageCallback_;
WriteCompleteCallback writeCompleteCallback_;
// 线程初始化时调用回调,可选
ThreadInitCallback threadInitCallback_;
// 标志是否启动
std::atomic_int started_;
// 保存所有连接
int nextConnId_;
ConnectionMap connections_;
用户创建TcpServer对象时,需指定baseLoop,需要监听的套接字,并设置自定义的回调和线程池中需要初始化的线程个数。
TcpServer::TcpServer(EventLoop* loop,
const InetAddress &listenAddr,
const std::string &nameArg,
Option option)
: loop_(CheckLoopNotNull(loop))
, ipPort_(listenAddr.toIpPort())
, name_(nameArg)
, acceptor_(new Acceptor(loop, listenAddr, option == kReusePort))
, threadPool_(new EventLoopThreadPool(loop, name_))
, connectionCallback_()
, messageCallback_()
, nextConnId_(1)
, started_(0)
{
// 传入新连接到来操作回调到acceptor
acceptor_->setNewConnectionCallback(std::bind(&TcpServer::newConnection, this,
std::placeholders::_1, std::placeholders::_2));
}
void setThreadInitcallback(const ThreadInitCallback &cb) { threadInitCallback_ = cb; }
void setConnectionCallback(const ConnectionCallback &cb) { connectionCallback_ = cb; }
void setMessageCallback(const MessageCallback &cb) { messageCallback_ = cb; }
void setWriteCompleteCallback(const WriteCompleteCallback &cb) { writeCompleteCallback_ = cb; }
void setThreadNum(int numThreads);
设置好后,调用start()
开启服务器
void TcpServer::start() {
if(started_++ == 0) {
// 开启线程池
threadPool_->start(threadInitCallback_);
// 由loop轮询运行acceptor封装的listen函数
loop_->runInLoop(std::bind(&Acceptor::listen, acceptor_.get()));
}
}
当新连接到来时,会调用TcpServer中的newConnection方法(怎么调用?可查看Acceptor.*
代码)
void TcpServer::newConnection(int sockfd, const InetAddress &peerAddr) {
// 从线程池中获取空闲线程
EventLoop *ioLoop = threadPool_->getNextLoop();
char buf[64] = {0};
snprintf(buf, sizeof buf, "-%s#%d", ipPort_.c_str(), nextConnId_);
++nextConnId_;
std::string connName = name_ + buf;
LOG_INFO("TcpServer::newConnection [%s] - new connection [%s] from %s \n",
name_.c_str(), connName.c_str(), peerAddr.toIpPort().c_str());
sockaddr_in local;
::bzero(&local, sizeof local);
socklen_t addrlen = sizeof local;
if(::getsockname(sockfd, (sockaddr*)&local, &addrlen) < 0) {
LOG_ERROR("sockets::getLocalAddr");
}
InetAddress localAddr(local);
// 创建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, std::placeholders::_1)
);
// 丢入loop中轮询连接建立完成回调
ioLoop->runInLoop(std::bind(&TcpConnection::connectEstableished, conn));
}