Muduo网络库解析---架构设计

news/2024/12/14 17:56:00/文章来源:https://www.cnblogs.com/itxt/p/18606993

前言

muduo库是陈硕个人开发基于reactor模式的tcp网络编程库。本人之前有学习过boost.asio网络库,故学习一下Muduo网络库,并分析它们之间的优缺点。

本系列将重点放在以下几件事情:

  1. 梳理Muduo的核心架构设计以及各个模块的职责
  2. 理解Muduo的事件驱动机制
  3. 理解Muduo的多线程模型
  4. 剖析作者精妙的代码设计思路并且重写其核心代码,将原来依赖boost库的地方都替换成C++11语法

下面列出主要讲解的模块

  1. 网络相关模块
    • Socket
    • InetAddress
    • TcpConnection
    • Acceptor
    • TcpServer
  2. 事件循环相关模块
    • EventLoop
    • Channel
    • Poller
    • EpollPoller
  3. 线程相关模块
    • Thread
    • EventLoopThread
    • EventLoopThreadPool
  4. 基础模块
    • Buffer
    • Timestamp
    • Logger

概述篇

一、Muduo网络库简介

Muduo网络库是一个基于非阻塞IO和事件驱动的C++高并发TCP网络库。它基于Reactor的事件处理模式,并且采用了one loop per thread的线程模型,即每个线程只运行了一个事件循环。这种模型使得Muduo能够充分利用多核CPU的性能,实现高效的网路通信。

Reactor事件处理模式

Reactor是这样的一种模式,它要求主线程只负责监听文件描述符上是否有事件发生,如果有的话立即通知工作线程。除此之外,主线程不做任何其他实质性工作。读写数据,接受新的连接,以及处理客户请求均在工作线程中完成。

二、基于muduo实现简易聊天服务器

实现

在使用muduo网络库的一个巨大优势在于:

业务逻辑与网络层的解耦。muduo是一个网络层框架,已经把网络层面的接受新的连接、收发数据等操作都封装好了,开发者可以只去关注业务逻辑而不必花费大量时间在底层网络通信的细节上。

这里介绍下面的代码会用到的两个模块:

  • 接受新连接:Muduo 的 TcpServer 模块自动处理新客户端的连接请求。
  • 连接的生命周期管理:通过 TcpConnection 管理每个连接的状态(建立、关闭)。

聊天服务器功能:

  1. 支持多个客户端同时连接。
  2. 广播消息:当某个客户端发送消息时,服务器将消息广播给所有其他客户端
  3. 管理客户端连接,支持连接建立和断开通知

ChatServer用来抽象这一过程,下面是ChatServer的声明

#include <muduo/net/TcpServer.h>
#include <muduo/net/EventLoop.h>
#include <muduo/base/Logging.h>
#include <set>
#include <string>class ChatServer {
public:// ChatServer的构造函数ChatServer(muduo::net::EventLoop* loop,const muduo::net::InetAddress& listenAddr);void start();private:// 连接新的客户端的回调函数void onConnection(const muduo::net::TcpConnectionPtr& conn);// 接受消息的回调函数void onMessage(const muduo::net::TcpConnectionPtr& conn,muduo::net::Buffer* buf,muduo::Timestamp time);// 管理Server的组件muduo::net::TcpServer server_;// // 保存连接的客户端 std::set<muduo::net::TcpConnectionPtr> connections_; // 保存连接的客户端
};

下面是ChatServer的定义

#include "ChatServer.h"using namespace muduo;
using namespace muduo::net;ChatServer::ChatServer(EventLoop* loop, const InetAddress& listenAddr): server_(loop, listenAddr, "ChatServer") {server_.setConnectionCallback(std::bind(&ChatServer::onConnection, this, std::placeholders::_1));server_.setMessageCallback(std::bind(&ChatServer::onMessage, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
}void ChatServer::start() {server_.start();
}void ChatServer::onConnection(const TcpConnectionPtr& conn) {if (conn->connected()) {LOG_INFO << "New connection from " << conn->peerAddress().toIpPort();connections_.insert(conn);} else {LOG_INFO << "Connection from " << conn->peerAddress().toIpPort() << " closed";connections_.erase(conn);}
}void ChatServer::onMessage(const TcpConnectionPtr& conn, Buffer* buf, Timestamp time) {std::string msg = buf->retrieveAllAsString();LOG_INFO << "Received message: " << msg;// 将消息广播给所有连接的客户端for (const auto& client : connections_) {client->send(msg);}
}

由上述代码可知,程序员只需要定义好接受新的连接,接受客户端发来的消息的回调函数就可以实现简易的聊天服务器。做到业务逻辑 与 网络层面 的 解耦。

接下来实现main函数

#include <iostream>
#include "ChatServer.h"int main()
{int port = 10086;muduo::net::InetAddress listenAddr(port);	// 监听Addrmuduo::net::EventLoop loop;ChatServer server(&loop, listenAddr);server.start();loop.loop();return 0;
}
cmake_minimum_required(VERSION 3.5.0)
project(QTalk VERSION 0.1.0 LANGUAGES C CXX)add_executable(QTalk 
main.cpp
ChatServer.cpp)set(EXECUTABLE_OUTPUT_PATH ../)target_link_libraries(QTalkmuduo_netmuduo_basepthread
)

测试运行

之后将程序运行起来后,利用Linux命令可以快速地进行TCP连接与收发数据:nc localhost 10086

Server会输出两条消息:

image-20241204190831951

第一条消息是muduo库输出的信息,第二条是在ChatServer::onConnection即接受新的连接后的回调函数输出的消息。

发送一条消息后,客户端会接收到同样的消息,在服务端这里:

image-20241204191031291

同理,第一条消息是muduo库输出的消息,第二条是ChatServer::onMessage即收到新的消息的回调函数输出的消息。

三、muduo的架构设计

muduo网络库是基于reactor事件处理模式的TCP网络库。

Reacor模式

Reactor模式的核心为:Event事件Reactor反应堆、Demultiplex事件分发器、EventHandler事件处理器

image-20241204195612430

Reactor 模式通过以上组件协作完成事件的处理,以下是其具体流程:

  1. 事件注册:
    • 应用程序将需要监听的事件及对应的事件处理器(EventHandler)注册到Reactor
  2. 事件循环:
    • Reactor启动循环,调用Demuliplex等待事件发生
  3. 事件检测:
    • 当事件就绪时,Demultiplex将事件返回给Reactor
  4. 事件分发:
    • Reactor根据事件类型找到对应的EventHandler,并触发处理
  5. 事件处理:
    • EventHandler执行具体的业务逻辑,如读取数据、处理消息等。

muduo框架架构解析

Muduo的架构以one loop per thread即每个线程都有一个事件循环设计,每个线程都有它自己的Reactor,负责该线程的所有事件。主线程(main reactor)负责监听新的连接,并把accept后的socket封装起来交付给其他线程的 sub reactor处理,之后各个sub reactor负责与该连接的所有读写事件。

image-20241204200817017

主从Reactor工作流程

主线程(main Reactor)

  • 职责:
    • 负责监听客户端的连接
    • 处理accept操作,接受新的客户端连接
    • 将接收到的客户端连接进行封装,通过负载均衡算法分配给线程池的sub Reactor
  • 工作流程:
    1. 主线程监听套接字的可读事件(即有新连接到来)
    2. 调用 accept 接受新连接
    3. 将新连接的 socket 封装为 TcpConnection 对象
    4. 将新连接分配给某个工作线程的 EventLoop

工作线程(sub Reactor)

  • 职责:
    • 独立运行EventLoop,负责管理其分配的客户端连接
    • 处理连接的读写以及超时等操作
  • 工作流程
    1. 主线程将新连接分配给工作线程后,工作线程接管该连接
    2. 工作线程的 EventLoop 注册该连接的事件(如可读、可写)
    3. 当连接上的事件触发时,调用对应的回调函数处理
    4. 处理完成后,根据需要更新事件监听状态

线程分配与负载均衡

Muduo使用线程池ThreadPool来管理多个工作线程,每个工作线程运行一个EventLoop。新连接分配策略采用简单的轮询算法(Round-Robin):

  1. 主线程维护一个线程池,每个线程绑定一个EventLoop
  2. 每次接收到新连接后,按照轮询算法选择一个线程,将连接分配到该线程对应的EventLoop
  3. 开发人员调用TcpServer::setThreadNum来设置工作线程的数量

四、总结

muduoOne Loop Per Thread模型通过主从Reactor的组合,合理分配了主线程和工作线程的职责,兼顾了性能与易用性。这种设计能够充分利用多核资源,同时屏蔽了复杂的底层网络细节,为开发者提供了一种简单、高效的开发体验。

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2024-2025-1学号20241309《计算机基础与程序设计》第十二周学习总结

作业信息这个作业属于哪个课程 2024-2025-1-计算机基础与程序设计这个作业要求在哪里 2024-2025-1计算机基础与程序设计第十二周作业这个作业的目标作业正文 2024-2025-1学号20241309《计算机基础与程序设计》第十二周学习总结教材学习内容总结 《C语言程序设计》第十一章: 一…

四阶魔方教程

四阶魔方玩法还原教程 四阶魔方的还原其实很简单,大家不要看我画了这么多图,写了这么多字就以为好像很难,其实我是为了让大家尽可能的不费脑力就能学会才讲得尽量具体一点。 我们用的是降阶法,基本的还原过程如下:1.中心块复原2.棱的合并3.按三阶魔方还原4.特殊情况校正四…

P6599 「EZEC-2」异或

写数学时发现的好题 给出n和l,构造一个数组,数组长度为l,满足数组中的数字在 1-n 之间贪心的想,直接放n会发生什么。不难发现,最终的答案其实是两两异或之和 放一个n,答案就是1(l-1)n; 放一个n,答案就是2(l-2)n; 其实就是x(l-x)n; 还可以更大吗? n写成二进制就…

使用AOP防止请求重复提交

使用AOP防止请求重复提交首先定义注解NoPepeatSubmit@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface NoRepeatSubmit {long value() default 1000*10; }定义AOP相关方法public class RepeatSubmitAspect {@Autowiredprivate StringRedisS…

三门峡知识付费系统服务热线

关于三门峡地区的知识付费系统及教育服务,虽然直接与三门峡地区的本地资源相关的信息比较有限,我们可以提供一些更宽广的信息和资源链接以帮助相关从业人员更好地了解知识付费系统和服务相关的背景信息与技术细节。例如,如果您在寻求构建或选择一个适合自身需求的知识付费系…

2024-2025-1 20241305《计算机基础与程序设计》第十二周学习总结

------------恢复内容开始------------ 作业信息这个作业属于哪个课程 2024-2025-1-计算机基础与程序设计(https://edu.cnblogs.com/campus/besti/2024-2025-1-CFAP))这个作业要求在哪里 2024-2025-1计算机基础与程序设计第十二周作业这个作业的目标 指针和数组作业正文 本博客…

微信小程序商城构建全栈应用

D:\PanDownload\【微信小程序】\微信小程序商城构建全栈应用 第1章 前言:不同的时代,不同的Web 1-1 前言与导语 导语 好的课程需要包含俩方面: 一:整体的思路与编程思想(大局观,AOP ,10~20%) 二:具体的编程知识与技巧(TP5,小程序,数据库等80%) books+code 1-2 产品所使用的技…

龍兄虎弟 综艺 All In One

龍兄虎弟 综艺 All In One 主持人:張菲、費玉清龍兄虎弟 综艺 All In One主持人:張菲、費玉清精彩片段 https://www.youtube.com/watch?v=fD1MxE9e3Bg&list=PLtww_vcpAB8pJn3goLppo42EDqjt8t1kh完整版 https://www.youtube.com/watch?v=67MJj22yMp0&list=PLRWrniKO…

UWB物理层实现-特殊汉明码纠错

根据802.15.4协议,chapter15.2.7,PHR部分的编码,除了一些控制参数外,在后面添加了6位单错纠正双错检测码(SECDED),用于纠错能力的提升,这6位汉明码为PHR部分提供了至少1bit的纠错能力,以及至少2bit的检错能力。此码块由汉明码构成,与一般汉明码不同的是,改码并没有穿…

NestJS导出API文档

在NestJS中,你可以使用@nestjs/swagger包来定义你的API文档,并且可以很容易地将这些文档转换为API调用。以下是一个简单的例子,展示如何使用NestJS和Swagger来创建一个API文档,并且如何生成API调用。 首先,安装@nestjs/swagger和swagger-ui-express:npm install @nestjs/…

python版本切换

1、搜索框输入:环境变量2、在下面的系统变量:选择path -> 点击:编辑3、将需要的python版本上移到上面,调整好后,一路点击确定关闭几个打开的窗口,保存环境变量配置。3、打开cmd,输入:python --version 确认版本是否切换成功

机器学习期末复习笔记

机器学习期末复习机器学习期末复习笔记 简介 主要注重决策树的计算,朴素贝叶斯,PCA降维的计算 笔记