【从零实现JsonRpc框架#2】Muduo库介绍

1.基本概念

Muduo 由陈硕大佬开发,是一个基于非阻塞IO事件驱动 的C++高并发TCP网络编程库 。它是一款基于主从Reactor 模型的网络库,其使用的线程模型是 one loop per thread

1.1 主从 Reactor 模型

  • 主 ReactorMainReactor,通常由 EventLoop 实现):
    • 负责监听新连接(accept 事件),通过 Acceptor 类实现。
    • 使用 epoll/poll 等多路复用机制监控监听套接字。
  • 从 ReactorSubReactor,多个 EventLoop 线程):
    • 每个 EventLoop 管理一组已建立的 TCP 连接(TcpConnection)。
    • 处理连接的读写事件、定时任务和用户回调。

1.2 One Loop Per Thread

  • 线程绑定 :每个 EventLoop 对象严格绑定到一个线程(通过 EventLoop::loop() 在所属线程运行)。
  • 资源隔离 :TCP 连接的生命周期由所属 EventLoop 管理,避免跨线程竞争。
  • 性能优化 :通过线程局部存储(ThreadLocal)实现高效的事件循环访问。

2.常见接口

2.1TcpServer 类基础介绍

Kotlin 复制代码
typedef std::shared_ptr<TcpConnection> TcpConnectionPtr;
typedef std::function<void (const TcpConnectionPtr&)> ConnectionCallback;
typedef std::function<void (const TcpConnectionPtr&, Buffer*, Timestamp)> MessageCallback;

class InetAddress : public muduo::copyable
{
public:
 	InetAddress(StringArg ip, uint16_t port, bool ipv6 = false);
};

class TcpServer : noncopyable
{
public:
    enum Option
    {
        kNoReusePort,
        kReusePort,
    };
    TcpServer(EventLoop* loop, const InetAddress& listenAddr, const string& nameArg, Option option = kNoReusePort);

    void setThreadNum(int numThreads);
    void start();
    
    // 当⼀个新连接建⽴成功的时候被调用 
    void setConnectionCallback(const ConnectionCallback& cb)
    { connectionCallback_ = cb; }
    
    // 消息的业务处理回调函数---这是收到新连接消息的时候被调用的函数 
    void setMessageCallback(const MessageCallback& cb)
    { messageCallback_ = cb; }
};
  • 职责:服务端入口,管理监听套接字和连接池。

  • 关键流程

    1. 构造时绑定 EventLoop(主 Reactor)。
    2. start() 启动监听,注册 Acceptor 到主 Reactor。
    3. 新连接到达时,通过轮询算法分配从 Reactor 管理。
  • TcpServer(EventLoop* loop, const InetAddress& listenAddr, const string& nameArg) :构造函数,loop 是事件循环对象,listenAddr 是监听地址,nameArg 是服务器名称。
  • void setThreadNum(int numThreads):设置服务器的工作线程数。默认为 1,即单线程模式。
  • void start():启动服务器,开始监听端口。
  • void setConnectionCallback(const ConnectionCallback& cb):设置连接回调函数,当有新连接或连接断开时调用。
  • void setMessageCallback(const MessageCallback& cb):设置消息回调函数,当有数据到达时调用。
  • void setWriteCompleteCallback(const WriteCompleteCallback& cb):设置写完成回调函数,当所有数据写入完成后调用。
Kotlin 复制代码
void setConnectionCallback(ConnectionCallback cb); // 连接建立/关闭回调
void setMessageCallback(MessageCallback cb);       // 消息到达回调

示例代码:

Kotlin 复制代码
#include <muduo/net/TcpServer.h>
#include <muduo/net/EventLoop.h>
#include <muduo/net/InetAddress.h>
#include <iostream>

void onConnection(const muduo::net::TcpConnectionPtr& conn) {
    if (conn->connected()) {
        std::cout << "New connection: " << conn->name() << std::endl;
    } else {
        std::cout << "Connection closed: " << conn->name() << std::endl;
    }
}

void onMessage(const muduo::net::TcpConnectionPtr& conn, muduo::net::Buffer* buf, muduo::Timestamp receiveTime) {
    std::string msg(buf->retrieveAllAsString());
    std::cout << "Received message: " << msg << std::endl;
    conn->send(msg);  // 回显消息
}

int main() {
    muduo::net::EventLoop loop;
    muduo::net::InetAddress listenAddr(8080);
    muduo::net::TcpServer server(&loop, listenAddr, "EchoServer");

    server.setConnectionCallback(onConnection);
    server.setMessageCallback(onMessage);

    server.setThreadNum(4);  // 使用 4 个工作线程
    server.start();

    loop.loop();
    return 0;
}

2.2EventLoop 类基础介绍

EventLoop 是 Muduo 库的核心类之一,负责事件循环(event loop)。每个线程只能有一个 EventLoop 实例,它负责监听和分发事件(如 I/O 事件、定时器事件等)。

常用接口:

  • void loop():启动事件循环,进入事件监听状态。
  • void quit():退出事件循环。
  • void runInLoop(const Functor& cb) :在当前 EventLoop 线程中执行回调函数 cb。如果调用者不在该线程中,则将回调函数加入队列,稍后执行。
  • void queueInLoop(const Functor& cb) :将回调函数 cb 加入队列,稍后在 EventLoop 线程中执行。
  • TimerId runAt(const Timestamp& time, const TimerCallback& cb) :在指定的时间点 time 执行回调函数 cb
  • TimerId runAfter(double delay, const TimerCallback& cb) :在指定的延迟时间 delay 后执行回调函数 cb
  • TimerId runEvery(double interval, const TimerCallback& cb) :每隔 interval 秒执行一次回调函数 cb

成员:

Kotlin 复制代码
std::unique_ptr<Poller> poller_;   // 底层 IO 多路复用(epoll/poll)
std::vector<Functor> pendingFunctors_; // 跨线程任务队列

核心方法

Kotlin 复制代码
void loop();          // 启动事件循环(必须在本线程调用)
void quit();		 // 停止循环
void runInLoop(Functor cb); // 跨线程安全的任务提交
TimerId runAfter(double delay, TimerCallback cb); // 定时器

代码示例:

Kotlin 复制代码
#include <muduo/net/EventLoop.h>
#include <iostream>

void print() {
    std::cout << "Hello, EventLoop!" << std::endl;
}

int main() {
    muduo::net::EventLoop loop;
    loop.runAfter(1.0, print);  // 1秒后执行 print 函数
    loop.loop();  // 启动事件循环
    return 0;
}

2.3TcpConnection 基础介绍

TcpConnection 表示一个 TCP 连接。它是 TcpServer 的内部类,用于管理客户端与服务器之间的连接。每个连接都有一个唯一的 TcpConnection 对象。

  • void send(const void* data, int len):发送数据到客户端。
  • void send(const StringPiece& message):发送字符串数据到客户端。
  • void shutdown():关闭连接(半关闭,停止发送数据,但仍然可以接收数据)。
  • void forceClose():强制关闭连接。
  • bool connected() const:判断连接是否处于连接状态。
  • std::string name() const:返回连接的名称(唯一标识符)。
  • EventLoop* getLoop() const :返回该连接所属的 EventLoop

特性:

  • 继承 std::enable_shared_from_this,依赖智能指针管理生命周期。
  • 通过 Channel 类注册到所属 EventLoopPoller
Kotlin 复制代码
void send(const void* data, size_t len);  // 线程安全的发送接口
void shutdown();                          // 半关闭连接(写端)
bool connected();						  // 判断当前连接是否正常

示例代码:

Kotlin 复制代码
void onMessage(const muduo::net::TcpConnectionPtr& conn, muduo::net::Buffer* buf, muduo::Timestamp receiveTime) {
    std::string msg(buf->retrieveAllAsString());
    std::cout << "Received message: " << msg << std::endl;
    if (msg == "quit\n") {
        conn->shutdown();  // 如果收到 "quit",关闭连接
    } else {
        conn->send(msg);  // 回显消息
    }
}

2.4TcpClient 类基础介绍

TcpClient 用于创建 TCP 客户端。它可以连接到远程服务器并与之通信。

常用接口

  • TcpClient(EventLoop* loop, const InetAddress& serverAddr, const string& nameArg) :构造函数,loop 是事件循环对象,serverAddr 是服务器地址,nameArg 是客户端名称。
  • void connect():发起连接请求。
  • void disconnect():断开连接。
  • void setConnectionCallback(const ConnectionCallback& cb):设置连接回调函数。
  • void setMessageCallback(const MessageCallback& cb):设置消息回调函数。

同步控制:

  • 使用 CountDownLatch 等待连接建立完成后再发送数据:
Kotlin 复制代码
CountDownLatch latch(1);
client.setConnectionCallback([&](const TcpConnectionPtr& conn) {
    if (conn->connected()) latch.countDown();
});
client.connect();
latch.wait();  // 等待连接成功

示例代码:

Kotlin 复制代码
#include <muduo/net/TcpClient.h>
#include <muduo/net/EventLoop.h>
#include <muduo/net/InetAddress.h>
#include <iostream>

void onConnection(const muduo::net::TcpConnectionPtr& conn) {
    if (conn->connected()) {
        std::cout << "Connected to server." << std::endl;
        conn->send("Hello, server!\n");
    } else {
        std::cout << "Disconnected from server." << std::endl;
    }
}

void onMessage(const muduo::net::TcpConnectionPtr& conn, muduo::net::Buffer* buf, muduo::Timestamp receiveTime) {
    std::string msg(buf->retrieveAllAsString());
    std::cout << "Received from server: " << msg << std::endl;
}

int main() {
    muduo::net::EventLoop loop;
    muduo::net::InetAddress serverAddr("127.0.0.1", 8080);
    muduo::net::TcpClient client(&loop, serverAddr, "EchoClient");

    client.setConnectionCallback(onConnection);
    client.setMessageCallback(onMessage);

    client.connect();
    loop.loop();
    return 0;
}

2.5.Buffer 类基础介绍

Buffer 是 Muduo 中用于处理网络数据缓冲区的类。它实现了动态缓冲区的功能,支持高效的读写操作。

常用接口

  • void append(const char* data, size_t len):向缓冲区追加数据。
  • size_t readableBytes() const:返回可读字节数。
  • size_t writableBytes() const:返回可写字节数。
  • const char* peek() const:返回缓冲区中可读数据的起始位置。
  • void retrieve(size_t len):从缓冲区中移除已读取的数据。
  • std::string retrieveAllAsString():将缓冲区中的所有数据取出并转换为字符串。
  • void prepend(const void* data, size_t len):在缓冲区前面插入数据。

示例代码:

Kotlin 复制代码
void onMessage(const muduo::net::TcpConnectionPtr& conn, muduo::net::Buffer* buf, muduo::Timestamp receiveTime) {
    while (buf->readableBytes() >= sizeof(int)) {
        const char* data = buf->peek();
        int be32 = *reinterpret_cast<const int*>(data);
        int host32 = muduo::net::sockets::networkToHost32(be32);  // 转换为本地字节序
        buf->retrieve(sizeof(int));
        std::cout << "Received integer: " << host32 << std::endl;
    }
}

3.总结

  • EventLoop:负责事件循环,监听 I/O 事件和定时器事件。
  • TcpServer:用于创建 TCP 服务器,监听客户端连接。
  • TcpConnection:表示一个 TCP 连接,负责管理客户端与服务器之间的通信。
  • TcpClient:用于创建 TCP 客户端,连接到远程服务器。
  • Buffer:用于处理网络数据缓冲区,支持高效的读写操作。

这些类是 Muduo 库的核心组件,通过它们可以轻松构建高性能的 TCP 网络应用

相关推荐
SuperW7 分钟前
Linxu实验五——NFS服务器
运维·服务器
promise5249 分钟前
JVM之jcmd命令详解
java·linux·运维·服务器·jvm·bash·jcmd
Bruce_Liuxiaowei22 分钟前
Day 5:Warp高级定制与自动化
运维·warp
溜达的大象32 分钟前
docker创建一个centOS容器安装软件(以宝塔为例)的详细步骤
运维·docker·容器
果子⌂39 分钟前
Linux系统入门第十二章 --Shell编程之正则表达式
linux·运维·服务器
海尔辛1 小时前
学习黑客5 分钟读懂Linux Filesystem Interaction Continued
linux·服务器·学习
学习2年半1 小时前
服务器mysql连接我碰到的错误
运维·服务器·mysql
涛涛6号1 小时前
轻量服务器与宝塔
运维·服务器
奋斗者1号2 小时前
浏览器自动化与网络爬虫实战:工具对比与选型指南
运维·爬虫·自动化
是垚不是土2 小时前
Kolla-Ansible搭建与扩容OpenStack私有云平台
linux·运维·服务器·云计算·ansible·openstack