总纲

每个muduo网络库有一个事件驱动循环线程池EventLoopThreadPool
每个线程池中有多个事件驱动线程EventLoopThread
每个线程运行一个EventLoop事件循环
每个EventLoop事件循环包含一个io复用Poller,一个计时器队列TimerQueue
每个Poller监听多个Channel,TimerQueue其实也是一个Channel
每个Channel对应一个fd,在Channel被激活后调用回调函数
每个回调函数是在EventLoop所在线程执行
所有激活的Channel回调结束后EventLoop继续让Poller监听
所以调用回调函数的过程中是同步的,如果回调函数执行时间很长,那么这个EventLoop所在线程就会等待很久之后才会再次调用poll。

整个muduo网络库实际上是由Reactor + 线程池实现的,线程池中每一个线程都是一个Reactor模型。在处理大并发的服务器任务上有很大优势
[

](https://blog.csdn.net/sinat_35261315/article/details/78329657)

1、类图

muduo - 图1

muduo关闭连接 muduo - 图2

2、整体简介

1、muduo目录 基础库

muduo
\—base
|— AsyncLogging.{h,cc} 异步日志 backend
|— Atomic.h 原子操作与原子整数
|— BlockingQueue.h 无界阻塞队列(生产者消费者队列)
|— BoundedBlockingQueue.h 有界阻塞队列
|— Condition {h,cc} 条件变量,与Mutex.h一同使用
|— copyable.h 一个空基类,用于标识(tag)值类型
|— CountDownLatch {h,cc} “倒计时门闩” 同步
|— CurrentThread {h,cc} 当前线程
|— Date {h,cc} Julian 日期库(即公历)
|— Exception {h,cc} 带 stack trace 的异常基类
|— FileUtil {h,cc} 用来操作文件。AppendFile用来写日志
|— GzipFile.h 封装了gzip的一些API
|— LogFile {h,cc} 日志文件
|— Logging {h,cc} 简单的日志,可搭配AsyncLogging使用
|— LogStream {h,cc} 流日志
|— Mutex.h
|— noncopyable.h
|— ProcessInfo 进程信息
|— Singleton.h 线程安全的singleton
|— StringPiece.h 从google开源代码借用的字符串参数传递类型
|— Thread {h,cc} 线程对象
|— ThreadLocal.h 线程局部数据
|— ThreadLocalSingleton.h 每个线程一个singleton
|— ThreadPool {h,cc} 简单的固定大小线程池
|— Timestamp {h,cc} UTC时间戳
|— TimeZone {h,cc} 时区与夏令时
|— Types.h 基本类型的声明,包括muduo::string
|— WeakCallback.h

2、muduo目录 网络库

Buffer

muduo
\—net
|— Acceptor.{h,cc} 接受器,用于服务端接受连接
|— boilerplate {h,cc}
|— Buffer {h,cc} 缓冲区,非阻塞IO必备
|— BUILD.bazel
|— Callbacks. {h,cc}
|— Channel {h,cc} 用于每个socket连接的事件分发
|— Connector {h,cc} 连接器,用于客户端发起连接
|— Endian.h 网络字节序与本机字节序的转换
|— EventLoop {h,cc} 事件分发器
|— EventLoopThread {h,cc} 新建一个专门用于EventLoop的线程
|— EventLoopThreadPool {h,cc} muduo默认多线程IO模型
|— InetAddress {h,cc} IP地址的简单封装
|— Poller {h,cc} IO multiplexing 的基类接口
|— Socket {h,cc} 封装socket描述符,负责关闭连接
|— SocketsOps{h,cc} 封装底层的socket API
|— TcpClient {h,cc} tcp客户端
|— TcpConnection {h,cc} muduo里最大的一个类
|— TcpServer {h,cc} tcp服务端
|— Timer {h,cc} 定时器
|— TimerId.h
|— TimerQueue {h,cc} 定时器回调
|— ZlibStream.h
  1. net部分使用base中的工具类实现更高层次的逻辑,网络编程无非是对socket和其使用的epoll/poll等进行封装,使其便于使用,屏蔽掉底层网络库的一些 "坑", 在满足了基础的网络IO之后,就需要考虑高性能,高并发的问题,muduo 的是由poll/epoll 这些IO复用模型构成,但是单个IO线程在面对大量请求时难免处理不过来,所以就需要结合多线程或者线程池,一个线程对应一个epoll进行网络IO,这样就可以充分利用硬件多核系统。从软硬两方面综合提升性能。net部分封装的较为彻底,对上层提供的接口简单易用,所以涉及复杂的内部处理,接下来就对其内部实现进行探究。<br />BufferInetAddress具有值语义,可以拷贝;其他class 都是对象语义,不可以拷贝

Buffer

仿Netty ChannelBuffer的buffer class,数据的读写通过buffer 进行。用户代码不需要调用read()/write(),只需要处理收到的数据和 准备好要发送的数据
生命期由TcpConnection控制

InetAddress

封装IPv4地址(end point),注意,它不能解析域名, 只认IP地址。因为直接用gethostbyname()解析域名会阻塞IO线程

EventLoop

事件循环(反应器Reactor),每个线程只能有一个 EventLoop实体,它负责IO和定时器事件的分派。它用eventfd()来异步唤醒,这有别于传统的用一对pipe()的办法。它用TimerQueue作为计时器管理,用Poller作为IO multiplexing

EventLoopThread

启动一个线程,在其中运行EventLoop::loop()

TcpConnection

整个网络库的核心,封装一次TCP连接,注意它不能发起连接
生命期依靠shared_ptr管理(即用户和库共同控制)

TcpClient

用于编写网络客户端,能发起连接,并且有重试功能

TcpServer

用于编写网络服务器,接受客户的连接

Channel

Channel是selectable IO channel,负责注册与响应IO事件,注意它不拥有file descriptor。它是Acceptor、Connector、EventLoop、 TimerQueue、TcpConnection的成员,生命期由后者控制.

Socket

Socket是一个RAIIhandle,封装一个filedescriptor,并在析构时关闭 fd。它是Acceptor、TcpConnection的成员,生命期由后者控制。 EventLoop、TimerQueue也拥有fd,但是不封装为Socket class

SocketsOps

SocketsOps封装各种Sockets系统调用

Poller

是PollPoller和EPollPoller的基类,采用“电平触发”的语意。 它是EventLoop的成员,生命期由后者控制

PollPoller EPollPoller

封装poll()和epoll()两种IO multiplexing后端。poll的存在价值是便于调试,因为poll(2)调用是上下文无关的,用 strace(1)很容易知道库的行为是否正确

Connector

用于发起TCP连接,它是TcpClient的成员,生命期由后者控制

Acceptor

用于接受TCP连接,它是TcpServer的成员,生命期由后者控制

TimerQueue

用timerfd实现定时,这有别于传统的设置 poll/epoll_wait的等待时长的办法。TimerQueue用std::map来管理Timer, 常用操作的复杂度是O(logN),N为定时器数目。它是EventLoop的成员,生命期由后者控制

EventLoopThreadPool

用于创建IO线程池,用于把TcpConnection分派到某个EventLoop线程上。它是TcpServer的成员,生命期由后者控制

3、Event Loop

4、Buffer

muduo buffer的设计要点:
1、对外表现为一块连续的内存(char* p,int len).方便客户代码的编写
2、其size()可以自动增长,以适应不同大小的消息。它不是一个fixed size array(例如 char buf[8192]).
3、内部以std:vector来保存数据,并提供相应的访问函数
4、input buffer, TcpConnection会从socket读取数据,然后写入input buffer.客户代码从input buffer读取数据。
5、output buffer 客户代码会把数据写入output buffer; TcpConnection 从output buffer读取数据并写入socket
muduo - 图3