C++ Muduo 库学习笔记
Muduo 简介
Muduo 是陈硕写的一个基于 Reactor 模型的 C++ 网络库,适合写高性能网络服务,如 HTTP Server、游戏服务器等。
- Reactor 模型:是一个事件驱动模型,当 IO 准备好(可读 / 可写)时,由内核通知,再去处理。核心思想是不阻塞,不轮询,全部基于回调。
Muduo 具有以下核心特点:
- 非阻塞 I/O + epoll(在 Mac 上用 kqueue):不管有没有数据,都立刻返回,不会卡住。
- 多线程 + one loop per thread 模型:epoll 是 Linux 下的高性能 I/O 多路复用机制,可以监听成千上万个 socket,只要其中一个有数据就通知,效率极高。每个线程只运行一个 epoll,且只处理 I/O,不做业务阻塞逻辑。
- 基于 TCP 的高层封装,提供 TcpServer/TcpClient:不需要写 socket、bind、listen、accept 等。
- C++ 回调式 API
- 线程安全、延迟队列、Buffer、日志库等配套设施
Muduo 使用方式
注意:以下均在 Linux 下使用。
安装
Muduo 依赖于 Boost 库,我们要先安装 Boost。
先把 Linux 系统下的 boost 源码 boost_1_69_0.tar.gz 通过命令下载下来,然后解压,如下:
1 | wget https://www.programmercarl.com/download/boost_1_69_0.tar.gz |
tar 解压完成后,进入源码文件目录,查看内容:
1 | cd boost_1_69_0 |
运行 bootstrap.sh 工程编译构建程序,需要等待一会。查看目录:
1 | ./bootstrap.sh |
后续要执行的命令:
1 | ./b2 |
然后是安装 Muduo,安装命令如下:
1 | wget https://www.programmercarl.com/download/muduo-master.zip |
编译运行
以上环境配置好后,因为我们使用的是第三方库,代码编译完成后需要链接相应的 .so 库,可以通过下面两种不同的方式链接:
- 可以直接终端命令行上链接
1 | g++ -o server muduo_server.cpp -lmuduo.net -lmuduo_base -lpthread |
- 由于之前配置时我们已经加入到 /usr/local/lib 了,因此可以直接 include。
服务器编程
muduo 库服务器编程流程:
- 组合 TcpServer 对象
- 创建 EventLoop 事件循环对象的指针,可以向 loop 上注册感兴趣的事件,相应事件发生 loop 会上报给我们
- 明确 TcpServer 构造函数需要的参数,输出服务器对应类的构造函数;
1 | TcpServer(EventLoop* loop, // 事件循环 |
- 在当前服务器类的构造函数中,注册处理连接断开的回调函数和处理读写事件的回调函数主要通过下面两个回调函数实现:
1 | void setConnectionCallback(const ConnectionCallback& cb) // 链接的创建与断开 |
简单说一下回调逻辑:也就是你写的处理逻辑被 Muduo 在事件触发时自动调用。
这样进行设置,当有新连接或断开连接时,就会调用 cb。
- 设置合适的服务器端线程数量,muduo 会自动分配 I/O 线程与工作线程。
- 开启事件循环 start()
muduo 服务器端编程代码如下:
这里我们实现的功能是,我们往服务器发送什么,服务器就发回什么。
1 |
|
接下来是运行及调试。
1 | g++ -std=c++11 -I/usr/include \ |
然后我们开两个终端,一个运行该代码模拟服务器,另一个使用 telnet 工具尝试连接:
1 | telnet 127.0.0.1 6000 |
可以看到正常回显了。
Muduo 核心方法
整个 Muduo 分为两大模块:
- base:包含基础设施:线程池、日志、时间类、回调、原子操作等。
- net:网络库:EventLoop、Channel、Poller、TcpServer、TcpClient 等。
接下来,我们要重点学习 Muduo 库核心七大模块:
EventLoop(事件循环)—— Reactor 的核心
每个线程一个 EventLoop (one loop per thread)
核心作用:
- 负责处理 I/O 事件(epoll)
- 调用用户回调(MessageCallback 等)
- 执行定时器任务(TimerQueue)
必须掌握的方法:
- loop():事件循环,程序在这里“死循环等待事件”
- quit():让 event loop 退出
- runInLoop(cb):在该 EventLoop 所在的线程安全运行一个任务
- queueInLoop(cb):加入任务队列,异步执行
- runAfter(x, cb):x 秒后执行回调
- runEvery(x, cb):每 x 秒执行一次
Channel(通道)——fd的事件管理器
fd(file descriptor,文件描述符),用于标识对象。
每个 TCP 连接底层都有一个 Channel,它负责:
- fd
- 关心的事件(EPOLLIN / EPOLLOUT)
- 回调函数(读、写、关闭、错误)
必须掌握的方法:
- setReadCallback(cb):注册读事件回调(数据抵达时使用)
- setWriteCallback(cb):注册写事件回调
- enableReading():向 Poller 注册 EPOLLIN
- enableWriting():向 Poller 注册 EPOLLOUT
- disableWriting():关闭写事件
- disableAll():移除所有事件
Poller(事件分发器)——封装 epoll/kqueue
关键方法:
- poll(timeout, activeChannels):等待事件
- updateChannel(channel):更新 fd 事件
- removeChannel(channel):删除 fd
TcpServer——服务器对象
这是写服务器的主要接口。
常用方法:
- start():启动服务器
- setThreadNum(n):设置 sub-loop(I/O线程)数量
- setConnectionCallback(cb):连接建立/关闭回调
- setMessageCallback(cb):消息到达回调
TcpConnection——每个连接一个对象
管理一个 TCP 连接的生命周期,包含:
- Channel(fd对应)
- 输入/输出缓冲区
- 回调函数
必须掌握的方法:
- send(data):发送数据
- shutdown():关闭写端(FIN)
- forceClose():强制关闭
- setMessageCallback(cb):读事件回调
- setCloseCallback(cb):写事件回调
Buffer(缓冲区)
Muduo 自己实现的智能 buffer。
常用方法:
- readFd(fd):从 fd 读入 buffer
- retrieve(n):取走 n 字节
- retrieveAll():清空 buffer
- append(data):追加数据
- peek():读取但不取走