Muduo 网络库 入门详解

news2024/12/23 2:18:49

文章目录

    • 1. 什么是 Muduo 网络库?
    • 2. Muduo 的核心架构
      • 2.1 EventLoop
      • 2.2 Channel
      • 2.3 Poller
      • 2.4 TimerQueue
      • 2.5 TcpServer 和 TcpConnection
      • 架构图
    • 3. Muduo 的工作原理
    • 4. 部分组件介绍
      • 4.1 ProtobufCodec
      • 4.2 ProtobufDispatcher
      • 4.3 muduo::net::EventLoop
      • 4.4 muduo::net::TcpServer
      • 4.5 总结
    • 4. 快速上手:从简单服务器开始
      • 4.1 创建一个简单的 Echo 服务器
      • 4.2 定时器使用示例
    • 5. Muduo 的多线程支持
    • 6. muduo 库的安装
      • 6.2 如何编译和安装 Muduo?


1. 什么是 Muduo 网络库?

Muduo 是一个现代化、高性能的 开源 C++ 网络库。

  • 主要目标:帮助开发者快速构建高性能的网络服务器。
  • 设计理念:基于事件驱动(Reactor 模式),充分利用 C++11 的特性(如智能指针、多线程)。
  • 应用场景:高并发场景,如聊天服务器、Web 服务、RPC 系统、实时通信等。

Muduo 的优势包括:

  • 高性能:采用多线程和 epoll 技术优化 I/O 性能。
  • 易用性:屏蔽底层细节,提供简单直观的 API。
  • 模块化:核心模块职责清晰,方便扩展和维护。

2. Muduo 的核心架构

Muduo 的核心是事件驱动的多线程服务器框架,它主要由以下几个模块组成:

2.1 EventLoop

  • 功能:事件循环的核心,负责监听和分发 I/O 事件、定时器事件。
  • 特点:每个线程有一个独立的 EventLoop,保证线程安全。

2.2 Channel

  • 功能:Channel 是文件描述符(fd)和 EventLoop 的桥梁。它负责:
    • 监视 fd 上的事件(读、写、关闭等)。
    • 在事件发生时,调用用户提供的回调函数。

2.3 Poller

  • 功能:PollerEventLoop 的底层组件,封装了 I/O 多路复用机制(如 epoll)。
  • 扩展性:支持不同操作系统的事件机制(epoll/kqueue)。

2.4 TimerQueue

  • 功能:高效管理定时任务。
  • 特点:基于小顶堆或时间轮的数据结构,实现高性能的定时器。

2.5 TcpServer 和 TcpConnection

  • TcpServer:封装服务器端操作(监听端口、接受连接等),是构建服务器的核心组件。
  • TcpConnection:代表一个客户端连接,提供接口用于数据收发和生命周期管理。

架构图

在这里插入图片描述


3. Muduo 的工作原理

Muduo 基于 Reactor 模式,核心是事件驱动。以下是其工作流程:

  1. 启动服务器:

    • 创建一个 EventLoop 实例作为主循环。
    • 创建一个 TcpServer 实例,设置回调函数(连接、消息处理)。
    • 调用 loop.loop() 开始事件循环。
  2. 事件监听:

    • 主线程监听新连接。
    • 每当有新连接到来,将其分配到工作线程处理。
  3. 事件分发与处理:

    • EventLoop 监听事件,通过 Poller 检测就绪的文件描述符。
    • 调用 Channel 的回调函数处理事件。
  4. 数据收发与连接管理:

    • 使用 TcpConnection 提供的接口收发数据。
    • 在连接断开时,自动清理资源。

4. 部分组件介绍

上面我们已经简单的介绍了muduo的核心架构,这里介绍一些其重要组件:

4.1 ProtobufCodec

ProtobufCodec 是 Muduo 库中用于处理 Protobuf 序列化和反序列化的组件,它主要负责将 Protobuf 消息与字节流之间进行转换,通常用于网络通信中通过 TCP 发送 Protobuf 编码的消息。

作用:

  • 序列化和反序列化ProtobufCodec 将 Protobuf 消息转换成字节流(序列化)以及将字节流解析回 Protobuf 消息(反序列化)。
  • 网络传输:它的主要功能是使 Muduo 能够在网络中发送和接收 Protobuf 格式的数据。网络数据往往以字节流的形式传输,而 Protobuf 消息是结构化的数据。ProtobufCodec 实现了字节流与 Protobuf 消息之间的转换。

使用场景:

  • 在使用 Muduo 进行网络通信时,可能需要将应用层的数据(如结构化的 Protobuf 消息)进行传输。这时 ProtobufCodec 便用来进行编码和解码。

示例代码

class ProtobufCodec : public muduo::net::Codec {
public:
    using ProtobufMessagePtr = std::shared_ptr<google::protobuf::Message>;

    ProtobufCodec() {}
    
    // 编码:Protobuf消息 -> 字节流
    void encode(muduo::net::Buffer* buf, const ProtobufMessagePtr& message) {
        std::string data;
        message->SerializeToString(&data);
        buf->append(data);
    }

    // 解码:字节流 -> Protobuf消息
    bool decode(muduo::net::Buffer* buf, ProtobufMessagePtr* message) {
        std::string data = buf->retrieveAllAsString();
        *message = std::make_shared<MyProtoMessage>();
        return (*message)->ParseFromString(data);
    }
};

4.2 ProtobufDispatcher

ProtobufDispatcher 负责将接收到的 Protobuf 消息根据类型分发到相应的回调函数

作用:

  • 消息分发:它根据 Protobuf 消息的类型将消息分发给不同的处理器。通常,我们会为每种类型的 Protobuf 消息注册一个处理函数,当接收到对应类型的消息时,ProtobufDispatcher 会调用相应的处理器函数。
  • 动态路由ProtobufDispatcher 实现了一种基于消息类型的动态路由机制,使得不同类型的消息能够被正确地处理。

使用场景:

  • 在需要处理多种类型 Protobuf 消息的应用中,ProtobufDispatcher 可以帮助根据不同的消息类型来选择不同的处理逻辑。

示例:

class ProtobufDispatcher {
public:
    template <typename T>
    void registerHandler(const std::function<void(const T&)>& handler) {
        handlers_[typeid(T).name()] = [handler](const google::protobuf::Message& msg) {
            handler(static_cast<const T&>(msg));
        };
    }

    void dispatch(const google::protobuf::Message& msg) {
        auto it = handlers_.find(typeid(msg).name());
        if (it != handlers_.end()) {
            it->second(msg);  // 调用对应的处理器
        }
    }

private:
    std::unordered_map<std::string, std::function<void(const google::protobuf::Message&)>> handlers_;
};

4.3 muduo::net::EventLoop

EventLoop 是 Muduo 库中核心的事件处理循环,它负责处理 I/O 事件、定时事件等,并且是整个事件驱动框架的核心。每个线程通常会有一个 EventLoop 实例,负责管理该线程的事件。

作用:

  • 事件循环EventLoop 主要负责在一个单独的线程中运行事件循环,监听 I/O 事件、定时器事件等。当这些事件发生时,它会调用相应的回调函数。
  • 异步 I/O 操作:通过 EventLoop,Muduo 实现了非阻塞的异步 I/O 操作,可以高效地处理多个并发连接。
  • 管理定时器EventLoop 可以注册定时器,定时器到期时会触发回调。

使用场景

  • 作为 Muduo 库的核心组件,EventLoop 用于管理网络连接、事件处理、定时任务等。

示例

void onConnection(const muduo::net::TcpConnectionPtr& conn) {
    if (conn->connected()) {
        conn->send("Hello, client!");
    }
}

int main() {
    muduo::net::EventLoop loop;
    muduo::net::TcpServer server(&loop, muduo::net::InetAddress(12345), "EchoServer");
    server.setConnectionCallback(onConnection);
    server.start();
    loop.loop();  // 启动事件循环
}

4.4 muduo::net::TcpServer

TcpServer 是 Muduo 中用于创建和管理 TCP 服务器的类。它负责接收来自客户端的连接请求,创建 TcpConnection 对象,并在连接建立后处理客户端的请求。

作用:

  • 监听和接受连接TcpServer 会在指定端口上监听客户端的连接请求。
  • 管理连接:每当有客户端连接到服务器时,TcpServer 会创建一个 TcpConnection 对象,并为该连接注册回调函数。
  • 设置回调函数:你可以为连接、消息接收、消息发送等事件注册回调函数,处理业务逻辑。

使用场景

  • TcpServer 用于构建高效的服务器程序,处理多个客户端的连接和请求。

示例

class EchoServer {
public:
    EchoServer(muduo::net::EventLoop* loop, const muduo::net::InetAddress& listenAddr)
        : server_(loop, listenAddr, "EchoServer") {
        server_.setConnectionCallback(std::bind(&EchoServer::onConnection, this, _1));
        server_.setMessageCallback(std::bind(&EchoServer::onMessage, this, _1, _2, _3));
    }

    void start() {
        server_.start();
    }

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

    void onMessage(const muduo::net::TcpConnectionPtr& conn, muduo::net::Buffer* buf, muduo::Timestamp time) {
        std::string msg = buf->retrieveAllAsString();
        conn->send(msg);  // Echo the message back to the client
    }

    muduo::net::TcpServer server_;
};

int main() {
    muduo::net::EventLoop loop;
    muduo::net::InetAddress listenAddr(12345);
    EchoServer server(&loop, listenAddr);
    server.start();
    loop.loop();  // 启动事件循环
}

4.5 总结

  • ProtobufCodec:负责 Protobuf 消息的序列化和反序列化,通常用于网络消息的处理。
  • ProtobufDispatcher:根据 Protobuf 消息的类型,将其分发给不同的回调函数进行处理。
  • EventLoop:管理事件循环,处理 I/O 事件和定时事件,是 Muduo 的核心组件之一。
  • TcpServer:提供 TCP 服务功能,监听端口并处理客户端连接,支持注册多种事件回调。

4. 快速上手:从简单服务器开始

4.1 创建一个简单的 Echo 服务器

下面是一个完整的 Echo 服务器代码:

#include <muduo/net/TcpServer.h>
#include <muduo/net/EventLoop.h>
#include <muduo/base/Logging.h>

using namespace muduo;
using namespace muduo::net;

// 当有新连接时调用
void onConnection(const TcpConnectionPtr& conn) {
    if (conn->connected()) {
        LOG_INFO << "New connection from " << conn->peerAddress().toIpPort();
    } else {
        LOG_INFO << "Connection " << conn->name() << " closed";
    }
}

// 当收到消息时调用
void onMessage(const TcpConnectionPtr& conn, Buffer* buffer, Timestamp receiveTime) {
    string msg = buffer->retrieveAllAsString();
    LOG_INFO << "Received: " << msg;
    conn->send(msg); // Echo 回去
}

int main() {
    Logger::setLogLevel(Logger::INFO);
    EventLoop loop;  // 创建事件循环
    InetAddress listenAddr(8080);  // 监听端口
    TcpServer server(&loop, listenAddr, "EchoServer"); // 创建服务器

    server.setConnectionCallback(onConnection);  // 设置连接回调
    server.setMessageCallback(onMessage);        // 设置消息回调
    server.start();  // 启动服务器
    loop.loop();     // 开始事件循环
    return 0;
}

运行流程

  1. 启动程序后,服务器监听 8080 端口。
  2. 当客户端连接时,onConnection 被触发。
  3. 收到消息后,onMessage 被触发,并将消息回显给客户端。

4.2 定时器使用示例

Muduo库 提供了简单高效的定时器功能:

#include <muduo/net/EventLoop.h>
#include <muduo/base/Logging.h>

using namespace muduo::net;

void onTimer() {
    LOG_INFO << "Timer triggered!";
}

int main() {
    EventLoop loop;  
    loop.runEvery(1.0, onTimer);  // 每秒执行一次 onTimer
    loop.loop();
    return 0;
}

5. Muduo 的多线程支持

Muduo 默认是单线程的,但通过线程池可以轻松扩展为多线程模式:

#include <muduo/net/TcpServer.h>
#include <muduo/net/EventLoop.h>
#include <muduo/net/EventLoopThreadPool.h>

using namespace muduo;
using namespace muduo::net;

void onConnection(const TcpConnectionPtr& conn) {
    if (conn->connected()) {
        LOG_INFO << "Connected from " << conn->peerAddress().toIpPort();
    } else {
        LOG_INFO << "Disconnected.";
    }
}

void onMessage(const TcpConnectionPtr& conn, Buffer* buf, Timestamp) {
    conn->send(buf->retrieveAllAsString());
}

int main() {
    EventLoop loop;
    InetAddress listenAddr(8080);
    TcpServer server(&loop, listenAddr, "MultiThreadedServer");

    server.setThreadNum(4);  // 设置 4 个工作线程
    server.setConnectionCallback(onConnection);
    server.setMessageCallback(onMessage);

    server.start();
    loop.loop();
    return 0;
}

6. muduo 库的安装

6.2 如何编译和安装 Muduo?

  1. 下载源码:
    git clone https://github.com/chenshuo/muduo.git
    
  2. 编译安装:
    cd muduo
    mkdir build && cd build
    cmake .. && make && sudo make install
    

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2257792.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Scratch游戏推荐 | 磁铁与磁场原理模型——探索科学的奥秘!

今天为大家推荐一款既有趣又富有教育意义的Scratch互动作品——《磁铁与磁场原理模型》&#xff01;由ps49student503-25制作&#xff0c;这款作品通过直观的方式展示了磁铁和磁场的相互作用&#xff0c;帮助玩家深入了解磁场的方向与强度。快来拖动磁铁&#xff0c;观察磁场如…

汽车总线协议分析-CAN总线

随着汽车工业的发展&#xff0c;汽车各系统的控制逐步向自动化和智能化转变&#xff0c;汽车电气系统变得日益复杂。许多车辆设计使用CAN、CAN-FD、LIN、FlexRay或SENT在电子控制单元(ECU)之间以及ECU与传感器&#xff0c;执行器和显示器之间进行通信。这些ECU之间的通信允许车…

十四、Pod的升级和回滚

当集群中的某个服务需要升级时,我们需要停止目前与该服务相关的所有Pod,然后下载新版本镜像并创建新的Pod。如果集群规模比较大,则这个工作变成了一个挑战,而且先全部停止然后逐步升级的方式会导致较长时间的服务不可用。Kubernetes提供了滚动升级功能来解决上述问题。 如…

Redis篇-1--入门介绍

1、Redis概述 ‌Redis&#xff08;Remote Dictionary Server&#xff09;&#xff0c;全称为远程字典服务。‌是一个开源的、使用C语言编写的、支持网络交互的、可基于内存也可持久化的Key-Value数据库。 Redis提供了多种数据类型的存储&#xff0c;来适应不同场景下的存储需…

游戏引擎学习第35天

开场介绍 今天的任务是继续改进一个虚拟的瓦片地图系统&#xff0c;使其适合处理更大的世界。我们希望这个系统能管理大范围的游戏世界&#xff0c;其中包含按需存储的小区域。昨天&#xff0c;我们介绍了“内存区域”的概念&#xff0c;用于管理持久性存储。我们计划今天继续…

Apache Echarts和POI

目录 Apache ECharts 介绍 入门 绘制一个简单的图表 Apache POI 介绍 通过POI创建Excel文件并且写入文件内容 通过POI读取Excel文件中的内容 导出Excel表格 Apache ECharts 介绍 Apache ECharts 是一款基于 Javascript 的数据可视化图表库&#xff0c;提供直观&#xf…

怎么配置点击deploy就能把jar包直接打到nexus私库上,以及怎么配置从私库下载jar包

一.怎么配置点击deploy就能把jar包直接打到nexus私库上 方式一:在pom文件配置私库地址 1.第一步&#xff0c;在pom文件配置仓库地址&#xff0c;用于 deploy 上传 releases 对应正式版的仓库 snapshots 对应快照版的仓库 如果你打的jar包是以 -SNAPSHOT 结尾的, 那么就会…

基于最新的Apache StreamPark搭建指南

一、StreamPark 的介绍 官方文档:Apache StreamPark (incubating) | Apache StreamPark (incubating) 中文文档:Apache StreamPark (incubating) | Apache StreamPark (incubating)Github地址:https://github.com/apache/incubator-streampark Apache StreamPark™ 是一个…

数字IC后端实现常见的physical only cell都有哪些?如何添加这些cell?

数字IC后端实现阶段常见功能cell有哪些&#xff1f;比如AND&#xff0c;AOI&#xff0c;NAND等。 physical cell有哪些&#xff1f;都是干什么用的&#xff1f; 数字后端零基础入门系列 | Innovus零基础LAB学习Day9 &#xff08;1&#xff09; well tap cells&#xff1a;防止…

Promise详解-1:初识Promise

最近在回顾ES6的知识&#xff0c;想整理下跟Promise相关的内容。我准备整一个Promise解读的系列&#xff0c;看看能深入到什么程度吧。由浅入深&#xff0c;先认识下Promise。 痛苦的回忆&#xff1a;回调地狱 假如现在让你维护一个“古老”的项目&#xff0c;缺少脚手架的加…

【css】基础(一)

本专栏内容为&#xff1a;前端专栏 记录学习前端&#xff0c;分为若干个子专栏&#xff0c;html js css vue等 &#x1f493;博主csdn个人主页&#xff1a;小小unicorn ⏩专栏分类&#xff1a;css专栏 &#x1f69a;代码仓库&#xff1a;小小unicorn的代码仓库&#x1f69a; &a…

共享GitLab中CICD自动生成的软件包

0 Preface/Foreword 1 分享软件包地址 为了方便给接收对象方便下载固件&#xff0c;在下载固件时候&#xff0c;而无需打开网页&#xff0c;直接输入地址&#xff0c;弹出的对话框是将固件另存为。 或者进入CICD页面&#xff0c;找到job&#xff0c;在Download的标签上单击右键…

【云贝教育Linux技术文章】CentOS停止维护后如何获取redhat 8.0 yum源?详细操作指南!

本文为云贝教育 刘老师 原创&#xff0c;请尊重知识产权&#xff0c;转发请注明出处&#xff0c;不接受任何抄袭、演绎和未经注明出处的转载。 众所周知&#xff0c;centos 7 在2024年6月30日&#xff0c;生命周期结束&#xff0c;官方不再进行支持维护&#xff0c;而很多环境一…

泷羽sec学习打卡-brupsuite5

声明 学习视频来自B站UP主 泷羽sec,如涉及侵权马上删除文章 笔记的只是方便各位师傅学习知识,以下网站只涉及学习内容,其他的都 与本人无关,切莫逾越法律红线,否则后果自负 关于brupsuite的那些事儿-web抓包和app抓包 常见的抓包工具有哪些&#xff1f;web抓包app抓包Android抓…

Nanolog起步笔记-9-log解压过程(3)寻找meta续

Nanolog起步笔记-9-log解压过程-3-寻找meta续 当前的目标新的改变decompressNextLogStatementmetadata查看业务面的log语句注释掉 runBenchmark();改过之后&#xff0c;2条记录之后&#xff0c;这里就直接返回了 小结 当前的目标 没有办法&#xff0c;还要继续。 当前的目标&a…

Flask使用长连接(Connection会失效)、http的keep-alive、webSocket。---GPU的CUDA会内存不足报错

Flask Curl命令返回状态Connection: close转keep-alive的方法 使用waitress-serve启动 waitress-serve --listen0.0.0.0:6002 manage:app 使用Gunicorn命令启动 gunicorn -t 1000 -w 2 -b 0.0.0.0:6002 --worker-class gevent --limit-request-line 8190 manage:appFlask使用f…

用友U8+ API接口使用教程

前言 U8和其他的公开的开放API接口有一些差异&#xff0c;他是需要先对接的到代理服务器&#xff0c;通过代理服务器进行对接&#xff0c;所以只要保证U8能上网就能对接&#xff0c;和畅捷通T的模式有点类似 流程&#xff1a; 注册成为开发者&#xff08;用于创建用友U8 API应…

xtu oj 1618 素数个数

文章目录 前言代码思路 前言 有点儿难&#xff0c;至少对我来说。去年考试我没写出来。 代码 #include<stdio.h> #include<stdbool.h> #include<stdlib.h>//加 math 那个头文件好像要加这个头文件&#xff0c;我之前编译错误过&#xff0c;血泪教训 #incl…

DAY3 构造函数

构造函数使用代码&#xff1a; #include <iostream> using namespace std; class Rec {const int length;int width; public:Rec():length(10){cout << "Rec无参构造函数" << endl;};Rec(int a,int b):length(a),width(b){cout << "Re…

分布式搜索引擎之elasticsearch基本使用2

分布式搜索引擎之elasticsearch基本使用2 在分布式搜索引擎之elasticsearch基本使用1中&#xff0c;我们已经导入了大量数据到elasticsearch中&#xff0c;实现了elasticsearch的数据存储功能。但elasticsearch最擅长的还是搜索和数据分析。 所以j接下来&#xff0c;我们研究下…