首页 > 其他分享 >Muduo网络库解析---架构设计

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

时间:2024-12-14 17:53:40浏览次数:4  
标签:架构设计 Muduo Reactor ChatServer --- muduo 线程 连接 客户端

前言

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);	// 监听Addr
    muduo::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(QTalk
    muduo_net
    muduo_base
    pthread
)

测试运行

之后将程序运行起来后,利用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的组合,合理分配了主线程和工作线程的职责,兼顾了性能与易用性。这种设计能够充分利用多核资源,同时屏蔽了复杂的底层网络细节,为开发者提供了一种简单、高效的开发体验。

标签:架构设计,Muduo,Reactor,ChatServer,---,muduo,线程,连接,客户端
From: https://www.cnblogs.com/itxt/p/18606993

相关文章

  • Transformers-pipline
    HFTransformersPipelinesPipelines接口方式任务名称参数名称参数描述sentiment-analysismodel指定使用的模型名称或路径。tokenizer指定使用的分词器名称或路径。framework选择使用的深度学习框架,"pt"表示PyTorch,"tf"表示TensorFlow。device设......
  • Data Fabric - Study Notes 8
         dataprofiling ......
  • yolov7源码解读1-训练前准备
    一、怎么解决图片输入尺度不统一的问题YOLOv7的矩形训练是指在训练时对输入图片进行尺寸调整,以提高模型处理长宽比差异较大的图片时的性能,同时避免过多的图像变形。具体来说,以下是矩形训练的处理过程:1.矩形训练的核心目标目标:尽量保留图片的原始长宽比例,减少因为强制拉......
  • Muduo网络库解析--网络模块(2)
    前文重写Muduo库实现核心模块的Git仓库注:本文将重点剖析Muduo网络库的核心框架,深入探讨作者精妙的代码设计思路,并针对核心代码部分进行重写,将原本依赖boost的实现替换为原生的C++11语法。需要说明的是,本文并不打算对整个Muduo库进行完整的重写。Muduo库源码链接......
  • 历年英语四六级真题+解析合集【2015-2024.6】
    前言英语四六级考试,犹如一座横亘在众多学子求学道路上的重要关卡,其重要性不言而喻。回首自己的备考历程,那是一段充满挑战与汗水的时光。在备考期间,我深知优质学习资源对于提升成绩的关键作用,于是四处寻觅,如同在知识的海洋中奋力打捞珍贵的宝藏。幸运的是,经过不懈努力,我终......
  • Google在架包分析-巴西4
    包信息之前在应用榜找的几个包都是一家的,一样的方案没啥好分析的。这次就换游戏榜里面找了个包,直接看图标找很好找。逆向分析安装运行,刚开始还是进的A面小游戏,然后我说来抓个包看下,重新打开了一下app,结果直接就B面了,给我整不会了。。。现在的上架方案都这么随意进b面......
  • 2024-2025-1学号20241309《计算机基础与程序设计》第十二周学习总结
    作业信息这个作业属于哪个课程2024-2025-1-计算机基础与程序设计这个作业要求在哪里2024-2025-1计算机基础与程序设计第十二周作业这个作业的目标作业正文2024-2025-1学号20241309《计算机基础与程序设计》第十二周学习总结教材学习内容总结《C语言程序设......
  • [学习笔记 #?] Bostan-Mori 算法和 常系数齐次线性递推
    目录[学习笔记#?]Bostan-Mori算法和常系数齐次线性递推Bostan-Mori算法常系数齐次线性递推(使用此算法)[学习笔记#?]Bostan-Mori算法和常系数齐次线性递推Bostan-Mori算法它本身用来求$[x^k]\frac{f(x)}{g(x)}$,其中\(f(x),g(x)\)的次数分别为\(n,m\),\(......
  • 基于 chat-uikit-react-native 实现一个 React Native 聊天 App
    一、前言本文分享了通过github源码快速实现一个聊天App。二、具体步骤Step1:配置开发环境如果您电脑没有ReactNative开发环境,请先按照ReactNative官网set-up-your-environment配置开发环境.Step2:下载源码Demo源码可前往github下载Step3:获取应用信息......
  • Python机器学习--决策树算法
    一、决策树原理决策树是用样本的属性作为结点,用属性的取值作为分支的树结构。决策树的根结点是所有样本中信息量最大的属性。树的中间结点是该结点为根的子树所包含的样本子集中信息量最大的属性。决策树的叶结点是样本的类别值。决策树是一种知识表示形式,它是对所有样本......