Muduo 网络库完整搭建教程(Linux 环境)
Muduo 是基于 Reactor 模式的 Linux 专属 C++ 网络库,搭建核心是安装依赖→编译源码→验证安装,以下是 Ubuntu/CentOS 通用步骤,包含环境配置、编译细节、验证测试和常见问题解决。
一、搭建前提
- 操作系统:Linux(推荐 Ubuntu 18.04+/CentOS 7+,muduo 依赖 epoll,不支持 Windows/macOS);
- 核心依赖:C++ 编译器(gcc 4.8+)、CMake(3.0+)、Boost 库(1.58+)、Git(拉取源码)。
二、分步搭建流程
步骤 1:安装基础编译工具与依赖
# 更新软件源
sudo apt update
# 安装基础编译工具(gcc/g++/make/cmake)
sudo apt install -y build-essential cmake
# 安装 Boost 库(muduo 依赖 boost-thread/boost-system)
sudo apt install -y libboost-dev libboost-thread-dev
# 安装 Git(拉取源码)
sudo apt install -y git
步骤 2:拉取 Muduo 源码
# 克隆官方仓库(若网络慢,可换国内镜像,如 gitee 镜像)
git clone https://github.com/chenshuo/muduo.git
# 进入源码目录
cd muduo
步骤 3:编译 Muduo 源码
Muduo 采用 CMake 构建,编译前需创建构建目录,避免污染源码:
# 创建 build 目录(规范编译目录)
mkdir build && cd build
# 配置 CMake(指定安装路径为 /usr/local,方便系统查找)
cmake .. -DCMAKE_INSTALL_PREFIX=/usr/local
# 编译(-j 后接线程数,建议等于 CPU 核心数,加速编译)
# 例如 4 线程:make -j4
make -j$(nproc)
步骤 4:安装 Muduo 库(关键)
编译完成后,将库文件和头文件安装到系统目录(/usr/local),方便后续项目链接:
sudo make install
步骤 5:验证安装是否成功
安装后会生成两类文件,检查是否存在即可确认:
# 检查头文件(muduo 核心头文件)
ls /usr/local/include/muduo/
# 检查库文件(静态库,muduo_base 基础库 + muduo_net 网络库)
ls /usr/local/lib/libmuduo_*.a
正常输出应包含:
- 头文件:
muduo/base/、muduo/net/等目录; - 库文件:
libmuduo_base.a、libmuduo_net.a。
三、测试搭建结果(编写简单程序验证)
服务器代码:
cpp
#include<muduo/net/TcpServer.h>
#include<muduo/net/EventLoop.h>
#include<functional>
#include<string>
#include<iostream>
using namespace muduo;
using namespace muduo::net;
using namespace std;
class ChatServer
{
TcpServer server_;
EventLoop* loop_;
//上报连接相关信息的回调函数
void onConnection(const TcpConnectionPtr&);
//上报读写事件相关信息的回调函数
void onMessage(const TcpConnectionPtr&,Buffer*,Timestamp);
public:
ChatServer(EventLoop*loop, const InetAddress& listenAddr,const string& nameArg);
void start();
};
void ChatServer::onConnection(const TcpConnectionPtr &conn)
{
if (conn->connected())
{
printf("ChatServer - %s connected\n", conn->peerAddress().toIpPort().c_str());
}
else
{
printf("ChatServer - %s disconnected\n", conn->peerAddress().toIpPort().c_str());
conn->shutdown();
}
}
void ChatServer::onMessage(const TcpConnectionPtr &conn, Buffer *buffer, Timestamp time)
{
string buff=buffer->retrieveAllAsString();
printf("ChatServer received message:%s\n", buff.c_str());
conn->send(buff);
}
ChatServer::ChatServer(EventLoop *loop, const InetAddress &listenAddr, const string &nameArg) : loop_(loop), server_(loop, listenAddr, nameArg)
{
server_.setConnectionCallback(
std::bind(&ChatServer::onConnection,this,_1)
);
server_.setMessageCallback(
std::bind(&ChatServer::onMessage,this,_1,_2,_3)
);
server_.setThreadNum(4);
}
void ChatServer::start()
{
server_.start();
}
int main(){
EventLoop loop;
InetAddress addr("127.0.0.1", 6000);
ChatServer server(&loop, addr, "ChatServer");
server.start();
loop.loop();
return 0;
}
代码讲解:
代码分为 3 个核心部分:
-
头文件与命名空间:引入 Muduo 核心组件 + 基础工具;
-
ChatServer 类封装:将服务器逻辑封装为类,包含连接 / 消息回调、服务器对象、Reactor 核心等;
-
main 函数:初始化 Reactor 循环、监听地址、启动服务器,是程序入口。
1. 头文件与命名空间(基础依赖)
cpp
#include<muduo/net/TcpServer.h> // Muduo 服务器核心类,封装监听/连接管理/线程池
#include<muduo/net/EventLoop.h> // Reactor 核心,负责 epoll 事件循环
#include<functional> // 用于 std::bind 绑定类成员函数为回调
#include<string>
#include<iostream>
using namespace muduo; // 简化 muduo 库命名
using namespace muduo::net; // 简化 muduo 网络模块命名
using namespace std; // 简化标准库命名
-
TcpServer:Muduo 封装的 TCP 服务器,无需手动处理epoll/accept,只需注册回调即可; -
EventLoop:Reactor 模式的核心,每个线程最多一个,负责事件监听、分发; -
functional:因为类的成员函数有隐含的this指针,需用std::bind绑定后才能适配 Muduo 的回调类型。
2. ChatServer 类定义(核心封装)
cpp
class ChatServer
{
// 成员变量:
TcpServer server_; // Muduo 服务器对象(核心)
EventLoop* loop_; // Reactor 事件循环指针(指向主线程的 EventLoop)
// 私有成员函数(回调函数):
void onConnection(const TcpConnectionPtr&); // 连接状态变化回调
void onMessage(const TcpConnectionPtr&,Buffer*,Timestamp); // 数据读写回调
public:
// 构造函数:初始化服务器对象、绑定回调、设置线程池
ChatServer(EventLoop*loop, const InetAddress& listenAddr,const string& nameArg);
void start(); // 启动服务器
};
-
成员变量设计:
-
server_:TcpServer是服务器的核心载体,封装了监听、连接管理、线程调度; -
loop_:指向主线程的EventLoop,传递给TcpServer后,服务器的 Acceptor(监听模块)会运行在该EventLoop中。
-
-
回调函数声明:
-
onConnection:处理 "连接建立 / 断开" 事件; -
onMessage:处理 "数据接收" 事件,参数包含连接对象、数据缓冲区、接收时间戳(Muduo 预定义的回调参数格式)。
-
3. onConnection 回调函数(连接事件处理)
cpp
void ChatServer::onConnection(const TcpConnectionPtr &conn)
{
if (conn->connected()) // 判断连接是否建立(三次握手完成)
{
printf("ChatServer - %s connected\n", conn->peerAddress().toIpPort().c_str());
}
else // 连接断开(四次挥手/网络异常)
{
printf("ChatServer - %s disconnected\n", conn->peerAddress().toIpPort().c_str());
conn->shutdown(); // 主动关闭连接(半关闭写端,释放资源)
}
}
-
核心参数
TcpConnectionPtr:是std::shared_ptr<TcpConnection>的别名,Muduo 用智能指针管理连接生命周期,避免野指针; -
关键接口:
-
conn->connected():判断当前连接是否处于 "已建立" 状态; -
conn->peerAddress():获取客户端的 IP:Port(InetAddress类型),toIpPort()转为字符串; -
conn->shutdown():主动关闭连接(关闭写端,触发 TCP 四次挥手,避免资源泄漏)。
-
4. onMessage 回调函数(数据读写处理)
cpp
void ChatServer::onMessage(const TcpConnectionPtr &conn, Buffer *buffer, Timestamp time)
{
string buff=buffer->retrieveAllAsString(); // 读取缓冲区所有数据并清空
printf("ChatServer received message:%s\n", buff.c_str());
conn->send(buff); // 回显数据给客户端(异步发送)
}
-
参数解析:
-
Buffer* buffer:Muduo 封装的缓冲区,解决 TCP 粘包 / 拆包问题(流式协议无消息边界); -
Timestamp time:数据接收完成的时间戳(示例中未使用,可用于日志 / 性能统计);
-
-
核心操作:
-
buffer->retrieveAllAsString():读取缓冲区中所有可读数据,转为字符串,并移动读指针(清空缓冲区); -
conn->send(buff):异步发送数据给客户端 ------ 若数据能一次性写入 socket,则直接发送;若写满,则存入写缓冲区,等待EPOLLOUT事件触发后继续发送(Muduo 自动处理,无需手动关注非阻塞写)。
-
5. ChatServer 构造函数(初始化核心逻辑)
cpp
ChatServer::ChatServer(EventLoop *loop, const InetAddress &listenAddr, const string &nameArg)
: loop_(loop), server_(loop, listenAddr, nameArg) // 初始化列表
{
// 绑定连接回调:将类成员函数 onConnection 转为 Muduo 可识别的回调类型
server_.setConnectionCallback(
std::bind(&ChatServer::onConnection,this,_1)
);
// 绑定消息回调:将类成员函数 onMessage 转为 Muduo 可识别的回调类型
server_.setMessageCallback(
std::bind(&ChatServer::onMessage,this,_1,_2,_3)
);
server_.setThreadNum(4); // 设置工作线程池大小为 4
}
-
初始化列表注意点 :类中
server_声明在loop_之前,但初始化列表先初始化loop_、后初始化server_------ 编译器会触发 "成员初始化顺序不匹配" 警告(不影响功能,但建议调整类成员声明顺序:先loop_,后server_)。 -
回调绑定核心(std::bind) :Muduo 的回调类型是
std::function,而类的成员函数有隐含的this指针,需用std::bind绑定:-
this:指向当前ChatServer对象,保证回调能调用到当前对象的成员函数; -
_1/_2/_3:占位符,对应回调函数的参数(onConnection有 1 个参数,onMessage有 3 个参数)。
-
-
setThreadNum(4):设置 Muduo 的工作线程池大小为 4------Muduo 采用 "单 Reactor 多线程" 模型:
-
主线程(Reactor 线程):处理
Acceptor监听事件(新连接建立); -
4 个工作线程:处理已建立连接的读写事件(
onMessage运行在工作线程)。
-
6. start 成员函数(启动服务器)
cpp
void ChatServer::start()
{
server_.start(); // 调用 TcpServer::start(),底层启动 Acceptor 监听端口 + 启动工作线程池
}
TcpServer::start()做两件核心事:- 启动
Acceptor:调用listen()监听指定端口,开始接收新连接; - 启动线程池:创建 4 个工作线程,每个线程绑定一个
EventLoop,用于处理连接的读写事件。
- 启动
7. main 函数(程序入口)
cpp
int main(){
EventLoop loop; // 创建主线程的 EventLoop(Reactor 核心,阻塞运行)
InetAddress addr("127.0.0.1", 6000); // 监听地址:127.0.0.1:6000
ChatServer server(&loop, addr, "ChatServer"); // 创建服务器对象
server.start(); // 启动服务器
loop.loop(); // 启动 Reactor 事件循环(阻塞,直到退出)
return 0;
}
-
核心流程:
-
EventLoop loop:主线程创建EventLoop,是整个服务器的 "心脏",负责 epoll 事件监听 / 分发; -
InetAddress addr:指定服务器监听的 IP 和端口(127.0.0.1 表示仅本地可访问,若需外网访问改为 0.0.0.0); -
ChatServer server:初始化服务器,绑定回调、设置线程池; -
server.start():启动监听和线程池; -
loop.loop():进入无限循环,调用epoll_wait等待事件(新连接、数据读写、信号等),并分发事件到对应回调 ------这行代码会阻塞主线程,直到服务器退出。
-
运行结果:
