本篇博客针对Acceptor类和TcpServer类做下小结。

博客代码来自于陈硕的muduo网络库,github地址https://github.com/chenshuo/muduo

学习笔记:

Acceptor类专注于做一件事情,就是在服务端接收所有的客户端请求,将连接成功的客户端交给TcpServer进一步处理。当服务端处于“1+N”多线程模式时,Acceptor的所有IO事件在“1”代表的线程中执行,连接进来的多个客户端在“N”代表的线程池中执行IO事件。

TcpServer类作为用户可见类,用户可以设置线程池中子线程数量,当有新连接进来时,TcpServer循环抽取一个子线程处理该连接的IO事件,从而保证各子线程的负载平衡。

Acceptor.h

#ifndef MUDUO_NET_ACCEPTOR_H
#define MUDUO_NET_ACCEPTOR_H#include <functional>#include "muduo/net/Channel.h"
#include "muduo/net/Socket.h"namespace muduo
{
namespace net
{class EventLoop;
class InetAddress;///
/// Acceptor of incoming TCP connections.
///
class Acceptor : noncopyable
{public:typedef std::function<void (int sockfd, const InetAddress&)> NewConnectionCallback;Acceptor(EventLoop* loop, const InetAddress& listenAddr, bool reuseport);~Acceptor();//设置新连接回调,Acceptor把新连接交给上层的TcpServer处理void setNewConnectionCallback(const NewConnectionCallback& cb){ newConnectionCallback_ = cb; }bool listenning() const { return listenning_; }void listen();//监听服务端private:void handleRead();//acceptSocket_有新来的连接EventLoop* loop_;      //通过loop_将acceptSocket_监听读事件注册到Poller中Socket acceptSocket_;  //等待socket等待客户端连接Channel acceptChannel_;//监听acceptSocket_描述符上的读事件NewConnectionCallback newConnectionCallback_;//TcpServer的新连接回调bool listenning_;int idleFd_;//预留空文件描述符,预防客户端连接数太多导致服务端无法连接
};}  // namespace net
}  // namespace muduo#endif  // MUDUO_NET_ACCEPTOR_H

Acceptor.cpp

#include "muduo/net/Acceptor.h"#include "muduo/base/Logging.h"
#include "muduo/net/EventLoop.h"
#include "muduo/net/InetAddress.h"
#include "muduo/net/SocketsOps.h"#include <errno.h>
#include <fcntl.h>
//#include <sys/types.h>
//#include <sys/stat.h>
#include <unistd.h>using namespace muduo;
using namespace muduo::net;Acceptor::Acceptor(EventLoop* loop, const InetAddress& listenAddr, bool reuseport): loop_(loop),acceptSocket_(sockets::createNonblockingOrDie(listenAddr.family())),//创建非阻塞socketacceptChannel_(loop, acceptSocket_.fd()),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));//当acceptSocket_有新连接时会调用该回调
}Acceptor::~Acceptor()
{//注销Poller中的acceptChannel_acceptChannel_.disableAll();acceptChannel_.remove();//关闭空文件描述符::close(idleFd_);
}void Acceptor::listen()
{loop_->assertInLoopThread();listenning_ = true;//acceptSocket_监听新连接acceptSocket_.listen();//当acceptSocket_监听到了新连接,acceptChannel_同时也获取到了读事件acceptChannel_.enableReading();
}void Acceptor::handleRead()
{loop_->assertInLoopThread();InetAddress peerAddr;//FIXME loop until no moreint connfd = acceptSocket_.accept(&peerAddr);if (connfd >= 0){// string hostport = peerAddr.toIpPort();// LOG_TRACE << "Accepts of " << hostport;if (newConnectionCallback_){//把新连接丢给TcpServer处理,Acceptor继续执行监听新连接的工作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);//把客户端接进来后立马关闭连接释放仅剩的1个描述符::close(idleFd_);//让这个描述符重新占用空文件idleFd_ = ::open("/dev/null", O_RDONLY | O_CLOEXEC);}}
}

TcpServer.h

#ifndef MUDUO_NET_TCPSERVER_H
#define MUDUO_NET_TCPSERVER_H#include "muduo/base/Atomic.h"
#include "muduo/base/Types.h"
#include "muduo/net/TcpConnection.h"#include <map>namespace muduo
{
namespace net
{class Acceptor;
class EventLoop;
class EventLoopThreadPool;///
/// TCP server, supports single-threaded and thread-pool models.
///
/// This is an interface class, so don't expose too much details.
class TcpServer : noncopyable
{public:typedef std::function<void(EventLoop*)> ThreadInitCallback;enum Option{kNoReusePort,kReusePort,};//TcpServer(EventLoop* loop, const InetAddress& listenAddr);TcpServer(EventLoop* loop,const InetAddress& listenAddr,const string& nameArg,Option option = kNoReusePort);~TcpServer();  // force out-line dtor, for std::unique_ptr members.const string& ipPort() const { return ipPort_; }const string& name() const { return name_; }EventLoop* getLoop() const { return loop_; }/// Set the number of threads for handling input.////// Always accepts new connection in loop's thread./// Must be called before @c start/// @param numThreads/// - 0 means all I/O in loop's thread, no thread will created.///   this is the default value./// - 1 means all I/O in another thread./// - N means a thread pool with N threads, new connections///   are assigned on a round-robin basis.void setThreadNum(int numThreads);  //设置线程池IO线程个数void setThreadInitCallback(const ThreadInitCallback& cb)//设置初始回调{ threadInitCallback_ = cb; }/// valid after calling start()std::shared_ptr<EventLoopThreadPool> threadPool(){ return threadPool_; }/// Starts the server if it's not listenning.////// It's harmless to call it multiple times./// Thread safe.void start(); //开启服务端监听/// Set connection callback./// Not thread safe.void setConnectionCallback(const ConnectionCallback& cb){ connectionCallback_ = cb; }/// Set message callback./// Not thread safe.void setMessageCallback(const MessageCallback& cb){ messageCallback_ = cb; }/// Set write complete callback./// Not thread safe.void setWriteCompleteCallback(const WriteCompleteCallback& cb){ writeCompleteCallback_ = cb; }private:/// Not thread safe, but in loopvoid newConnection(int sockfd, const InetAddress& peerAddr);/// Thread safe.void removeConnection(const TcpConnectionPtr& conn);/// Not thread safe, but in loopvoid removeConnectionInLoop(const TcpConnectionPtr& conn);typedef std::map<string, TcpConnectionPtr> ConnectionMap;EventLoop* loop_;  //处理acceptor_的IO事件const string ipPort_;const string name_;std::unique_ptr<Acceptor> acceptor_; //循环监听所有客户端的连接请求std::shared_ptr<EventLoopThreadPool> threadPool_;//线程池,已连接客户端的IO事件ConnectionCallback connectionCallback_;          //连接回调MessageCallback messageCallback_;                //消息回调WriteCompleteCallback writeCompleteCallback_;    //写完成回调ThreadInitCallback threadInitCallback_;          //线程初始回调AtomicInt32 started_;//启动标志// always in loop threadint nextConnId_;//记录客户端个数ConnectionMap connections_;//保存客户端连接
};}  // namespace net
}  // namespace muduo#endif  // MUDUO_NET_TCPSERVER_H

TcpServer.cpp

#include "muduo/net/TcpServer.h"#include "muduo/base/Logging.h"
#include "muduo/net/Acceptor.h"
#include "muduo/net/EventLoop.h"
#include "muduo/net/EventLoopThreadPool.h"
#include "muduo/net/SocketsOps.h"#include <stdio.h>  // snprintfusing namespace muduo;
using namespace muduo::net;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_每收到一个客户端的连接,就将连接转给TcpServer::newConnection()处理acceptor_->setNewConnectionCallback(std::bind(&TcpServer::newConnection, this, _1, _2));
}TcpServer::~TcpServer()
{loop_->assertInLoopThread();LOG_TRACE << "TcpServer::~TcpServer [" << name_ << "] destructing";//逐个销毁映射表里的连接for (auto& item : connections_){TcpConnectionPtr conn(item.second);/*reset后conn指向的对象引用计数减为1,待执行完for循环后conn这个栈对象被销毁,引用计数减为0,至此连接彻底被销毁*/item.second.reset();conn->getLoop()->runInLoop(std::bind(&TcpConnection::connectDestroyed, conn));}
}//设置IO线程池子线程个数
void TcpServer::setThreadNum(int numThreads)
{assert(0 <= numThreads);threadPool_->setThreadNum(numThreads);
}void TcpServer::start()
{if (started_.getAndSet(1) == 0){//开启线程池,准备处理服务端的IO事件threadPool_->start(threadInitCallback_);//acceptor_开启监听assert(!acceptor_->listenning());loop_->runInLoop(std::bind(&Acceptor::listen, get_pointer(acceptor_)));}
}void TcpServer::newConnection(int sockfd, const InetAddress& peerAddr)
{loop_->assertInLoopThread();//给新连接进来的socket分配一个loop,以后该socket上所有的IO操作都在此loop中完成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//至此conn与ioLoop绑定,conn上的IO事件都将在ioLoop中执行TcpConnectionPtr conn(new TcpConnection(ioLoop,connName,sockfd,localAddr,peerAddr));connections_[connName] = conn;//给新连接进来的socket设置回调conn->setConnectionCallback(connectionCallback_);conn->setMessageCallback(messageCallback_);conn->setWriteCompleteCallback(writeCompleteCallback_);conn->setCloseCallback(std::bind(&TcpServer::removeConnection, this, _1)); // FIXME: unsafeioLoop->runInLoop(std::bind(&TcpConnection::connectEstablished, conn));
}void TcpServer::removeConnection(const TcpConnectionPtr& conn)
{// FIXME: unsafeloop_->runInLoop(std::bind(&TcpServer::removeConnectionInLoop, this, conn));
}void TcpServer::removeConnectionInLoop(const TcpConnectionPtr& conn)
{loop_->assertInLoopThread();LOG_INFO << "TcpServer::removeConnectionInLoop [" << name_<< "] - connection " << conn->name();//删除映射表的连接size_t n = connections_.erase(conn->name());(void)n;assert(n == 1);EventLoop* ioLoop = conn->getLoop();//注销socket在Poller中的监听ioLoop->queueInLoop(std::bind(&TcpConnection::connectDestroyed, conn));
}

muduo网络库之Acceptor、TcpServer相关推荐

  1. muduo网络库学习(七)用于创建服务器的类TcpServer

    目前为止,涉及到的绝大多数操作都没有提及线程,EventLoop,Poller,Channel,Acceptor,TcpConnection,这些对象的执行都是在单独线程完成,并没有设计多线程的创建销 ...

  2. 【Muduo源码剖析笔记】 网络库之Acceptor、Connector

    [Muduo源码剖析笔记] 网络库之Acceptor.Connector Acceptor typedef std::function<void (int sockfd, const InetA ...

  3. muduo网络库学习笔记(五) 链接器Connector与监听器Acceptor

    目录 muduo网络库学习笔记(五) 链接器Connector与监听器Acceptor Connector 系统函数connect 处理非阻塞connect的步骤: Connetor时序图 Accep ...

  4. muduo网络库学习(八)事件驱动循环线程池EventLoopThreadPool

    muduo是支持多线程的网络库,在muduo网络库学习(七)用于创建服务器的类TcpServer中也提及了TcpServer中有一个事件驱动循环线程池,线程池中存在大量线程,每个线程运行一个Event ...

  5. muduo网络库源码阅读Step by Step

    Posted on: Nov 26 2015 Categories: muduo C++ Tags: muduo 一般写服务端程序都需要有一个称手的网络库来帮我们处理琐碎的网络通信细节,比如连接的建立 ...

  6. 基于C++11的muduo网络库

    文章目录 写在前面 项目编译问题 库安装的问题 项目测试代码 关于压力测试 项目概述 muduo网络库的reactor模型 muduo的设计 muduo各个类 辅助类 NonCopyable Time ...

  7. muduo网络库的封装

    一.基础socket编程 网络编程的底层离不开socket,其处理流程表示如下: int sockfd = socket(AF_INET, SOCK_STREAM, 0);struct sockadd ...

  8. Muduo网络库核心梳理

    Muduo网络库 Muduo网络库本身并不复杂,是一个新手入门C++面向对象网络编程的经典实战项目.但是,新手在刚刚上手读代码的时候,非常容易陷入代码的汪洋大海,迷失方向.本文旨在简要梳理Muduo网 ...

  9. muduo网络库学习总结:基本架构及流程分析

    muduo网络库学习:基本架构及流程分析 基本架构 Basic Reactor Mutiple Reactor + ThreadPool muduo库的基本使用 基本结构介绍 EventLoop类 P ...

最新文章

  1. HD Piggy-Bank完全背包
  2. B-树、B+树、B*树详解
  3. 【默认加入持久化机制,防止消息丢失,v0.0.3】对RabbitMQ.Client进行一下小小的包装,绝对实用方便...
  4. 信息学奥赛一本通C++语言——1035:等差数列末项计算
  5. HDU2022 海选女主角【最值】
  6. python经典题库和答案_Python99道经典练习题答案.docx
  7. matlab 码表,0-254 ascii 码表
  8. web前端三大主流框架分析对比
  9. SegNet 论文解析
  10. 昇腾modelzoo复现yolov4_v1(模型训练+网络定义)
  11. Python图像处理库PIL中图像格式转换(二)
  12. iQOO手机如何打开高清通话volte?
  13. 亮剑java web_为什么《亮剑Java Web 项目开发案例导航》第二个项目运行不了?
  14. 【Eclipse】--Eclipse简介和安装
  15. C# 获得本机IP、端口等信息地址以及服务器IP信息
  16. 被各种注解搞晕了?那快来看看Spring Bean注解详解!
  17. 扦插技术图解_桂花的扦插方法(图解)
  18. 专项测试实战 | 如何测试 App 流畅度(基于 FPS 和丢帧率)?
  19. centos播放器解码器下载
  20. js向html中指定位置追加文本,JavaScript中如何添加文本节点?

热门文章

  1. 从空乘转行网络安全,我入行就拿到月薪13k!
  2. 国内服务器和香港服务器有什么区别?有4点不同之处
  3. Flutter 动画
  4. 微机原理知识点总结(第一章-微型计算机系统)
  5. CSS大道至简--读《CSS禅意花园》
  6. 一文了解Perl语言
  7. 【C语言】寄存器变量
  8. 中国移动推出短信小程序
  9. JAVA求第二小的整数
  10. Python总结篇——知识大全