当前位置: 首页 > news >正文

websevere服务器从零搭建到上线(四)|muduo网络库的基本原理和使用

文章目录

  • muduo源码编译安装
  • muduo框架讲解
  • muduo库编写服务器代码示例
    • 代码解析
      • 用户连接的创建和断开回调函数
      • 用户读写事件回调
    • 使用vscode编译程序
      • 配置c_cpp_properties.json
      • 配置tasks.json
      • 配置launch.json
      • 编译
  • 总结

muduo源码编译安装

muduo依赖Boost库,所以我们应该先编译安装boost,方法如下:
https://blog.csdn.net/QIANGWEIYUAN/article/details/88792874
随后可以编译安装muduo,方法如下:
https://blog.csdn.net/QIANGWEIYUAN/article/details/89023980

muduo的底层其实就是采用nonblock io + one loop per thread 以及 epoll + 线程池的框架,采用sub-Reactor模式。
该方案的特点就是one loop per thread,有一个main reactor负载accept连接,然后把连接分发到某个sub reactor(采用round-robin的方式选择sub reactor),该链接的所有操作都在那个sub reactor所处的线程中完成,多个链接可能被分派到多个线程中,以充分利用CPU。
Reactor poll的大小是固定的,根据CPU的核心数目确定

//设置EventLoop的线程个数,底层通过EventLoopThreadPool线程池管理线程类EventLoopThread
_server.setThreadNum(10);

一个Base IO thread负责accept新的连接,接收到新的连接以后,采用轮询的方式在reactor pool中找到合适的sub reactor将这个链接挂载上去,这个链接上的所有任务都在这个sub reactor上完成。
如果有过多的CPU I/O的计算任务,可以提交到创建的ThreadPool线程池中专门处理耗时的计算任务

muduo框架讲解

在这里插入图片描述
无论是什么语言,想要做到高并发,
首先要有一个IO线程,它里面有一个epoll,我们叫做main Reactor,它的作用就是建立新用户的连接,新用户的连接上之后,会把这些连接通过一定的负载算法分发给不同的工作线程。

这些工作线程就是用来处理已连接用户的读写事件,一般这些线程的数量会和CPU的核数对等。尽量做到高并发。
用IO复用的好处就是一个线程可以监听多个套接字,尤其是对于连接量大而活跃量少(一般都是这样的场景),所以epoll有非常大的性能优势。
如果工作线程的每一个epoll所监听的连接用户要做比较耗时的IO操作(传送文件、音视频),我们可以在工作线程内单独开一个线程。如果我们不单独开线程的话,工作线程会阻塞在IO操作中,无法监听其他依然注册在epoll树上的fd的读写时间

muduo源代码有很多非常好的封装思想,里面大量得使用了智能指针、绑定器、函数对象、回调机制等等,可以做到模块化的解耦和软件的高内聚低耦合

muduo库编写服务器代码示例

muoduo库的使用需要链接 libmuduo_base.so libmuduo_net.so libpthread.so
动态库在linux系统默认的路径是
/usr/lib
/usr/local/lib
如果动态库放在这个路径下不需要在cmake中添加搜索路径,因为他们已经处于环境变量中。

//由于依赖关系的存在,net依赖于base,base依赖于pthread,连接顺序不能改变
-lmuduo_net -lmuduo_base -lpthread

使用实例代码如下:

/*
muduo网络库给用户提供了两个主要的类
TcpServer:用于编写服务器程序的
TcpClient:用于编写客户端程序的epoll + 线程池
好处:能够把网络IO的代码和业务代码区分开,muduo库已经把网络端的代码封装好了。
业务代码暴露在两个,我们也只关心这两件事: 用户的连接和断开  用户的可读写事件
*/#include <muduo/net/TcpServer.h>
#include <muduo/net/EventLoop.h>
#include <iostream>
#include <functional>
#include <string.h>
using namespace std;
using namespace muduo;
using namespace muduo::net;
using namespace placeholders;/*基于muduo网络库开发服务器程序
1.组合TcpServer对象
2.创建EventLoop时间循环对象的指针
3.明确TcpServer的构造函数需要什么参数,输出ChatServer的构造函数
4.在当前服务器类的构造函数中,注册处理连接的回调函数和处理读写事件的回调函数
5.设置合适的服务端线程数量,muduo库会自己划分I/O线程和worker线程
*/ 
class ChatServer {
public:ChatServer(EventLoop* loop, //事件循环const InetAddress& listenAddr, //IP+Portconst string& nameArg)  //服务器名字:_server(loop, listenAddr, nameArg), _loop(loop) {//给服务器注册用户连接的创建和断开回调_server.setConnectionCallback(std::bind(&ChatServer::onConnection, this, _1));//给服务器注册用户读写事件回调_server.setMessageCallback(std::bind(&ChatServer::onMessage, this, _1, _2, _3));//设置服务器端的服务线程  设置为2,1个IO线程,1个worker线程_server.setThreadNum(2);    }// 开启事件循环void start() {_server.start();}
private://专门处理用户的链接创建和断开void onConnection(const TcpConnecitonPtr &conn) {if (conn->connected()) {cout << conn->peerAddress().toIpPort() << " -> " <<<< conn->localAddress().toIpPort() << "state:online" <<endl;} else {cout << conn->peerAddress().toIpPort() << " -> " <<<< conn->localAddress().toIpPort() << "state:offline" <<endl;conn->shutdown(); //连接断开后,回收fd资源close(fd)//_loop->quit(); //通知事件循环停止运行,这通常是在准备关闭服务器或者结束程序时执行的操作}}//专门处理用户的读写事件void onMessage(const TcpConnection &conn, //这是我们的连接,可以读数据也可以写数据 Buffer *buf, // 用户的缓冲区Timestamp time) {    //接受数据的时间信息string buf = buffer->retrieveAllAsString(); //可以把其中接收到的数据全部接收到自己的字符串当中cout << "recv data: " << buf << " time: " << time.toString() << endl;conn->send(buf);}TcpServer _server; //#1EventLoop *_loop;  //#2 把它看作epoll}int main () {EventLoop loop; //epollInetAddress addr("127.0.0.1", 10000);ChatServer server(&loop, addr, "ChatServer");server.start(); //listenfd epollctl=>epollloop.loop;      //epoll_wait以阻塞方式等待新用户连接,已连接用户的读写事件等return 0;
}

代码解析

以上代码就已经实现了一个高性能高并发的服务器程序,muduo库已经为我们把网络端封装好了,我们只需要进行应用层业务逻辑的代码编写。

在例子中,其实我们只需要关注两个函数的编写:

void onConnection(const TcpConnecitonPtr &conn);
void onMessage(const TcpConnection &conn, //这是我们的连接,可以读数据也可以写数据 Buffer *buf, // 用户的缓冲区Timestamp time) //接受数据的时间信息

也就是用户连接的创建和断开回调函数、用户读写事件回调。

用户连接的创建和断开回调函数

//专门处理用户的链接创建和断开
void onConnection(const TcpConnecitonPtr &conn) {if (conn->connected()) {cout << conn->peerAddress().toIpPort() << " -> " <<<< conn->localAddress().toIpPort() << "state:online" <<endl;} else {cout << conn->peerAddress().toIpPort() << " -> " <<<< conn->localAddress().toIpPort() << "state:offline" <<endl;conn->shutdown(); //连接断开后,回收fd资源close(fd)//_loop->quit(); //通知事件循环停止运行,这通常是在准备关闭服务器或者结束程序时执行的操作}}

本案例中,我们在连接后打印远端的相关信息,断开后打印远端相关信息(这里就是典型的业务层逻辑)。

用户读写事件回调

这里写的是接受读缓冲区(读缓冲区的数据是客户端写入的)的数据,并且打印到终端。

//专门处理用户的读写事件
void onMessage(const TcpConnection &conn, //这是我们的连接,可以读数据也可以写数据 Buffer *buf, // 用户的缓冲区Timestamp time) {    //接受数据的时间信息string buf = buffer->retrieveAllAsString(); //可以把其中接收到的数据全部接收到自己的字符串当中cout << "recv data: " << buf << " time: " << time.toString() << endl;conn->send(buf);}

如果是浏览器的话,在这里我们在读写事件回调中去解析浏览器的http消息,然后根据http的请求,组织http响应在发送回给浏览器(这就是所谓的业务代码)。

使用vscode编译程序

配置c_cpp_properties.json

按F1,这里可以把配置的对话框打印出来

{"configurations": [{"name": "Linux","includePath": ["${workspaceFolder}/**$"],"defines": [],"compilerPath": "/usr/bin/gcc","cStandard": "c11", "intelliSenseMode": "clang-x64"}],"version": 4
}

这里的配置文件中,若果我们去执行一条编译命令,需要写

//gcc -I头文件搜索路径 -L库文件搜索路径 -lmuduo_net库名称

一般我们可以在"includePath": []中加头文件、库文件的搜索路径(/usr/include /usr/local/include默认包含),库的名称在配置tasks.json;
还可以添加C++标准"cppStandard": "c++17"

配置tasks.json

ctrl+shift+B(build)可以看到构建项目的配置文件,点击齿轮即可
在这里插入图片描述
在这里有一个tasks.json配置文件。

	"version": "2.0.0","tasks": [{"type": "shell","label": "g++ build active file","command": "user/bin/g++","arg": ["-g","${file}","-o","${fileDirname}/${fileBasenameNoExtension}",]"options": {"cwd": "/bsr/bin"},"problemMatcher": ["$gcc"],"group": "build" }]

我们的-lmuduo_net库名称就是写在tasks.json文件中的"arg"

"arg": ["-g","${file}","-o","${fileDirname}/${fileBasenameNoExtension}","-lmuduo_net","-lmuduo_base","-lpthread"
]	

配置launch.json

只有调试的时候采用,我们最好使用gdb调试

编译

配置好c_cpp_properties.json和配置tasks.json后。
ctrl+shift+B(build)然后直接点击蓝色的地方而不是点击齿轮,就可以进行编译啦。
在这里插入图片描述

总结

这样,我们就很简单得写出了一个健壮的、基于事件驱动的、IO复用的高并发高性能服务器。

相关文章:

websevere服务器从零搭建到上线(四)|muduo网络库的基本原理和使用

文章目录 muduo源码编译安装muduo框架讲解muduo库编写服务器代码示例代码解析用户连接的创建和断开回调函数用户读写事件回调 使用vscode编译程序配置c_cpp_properties.json配置tasks.json配置launch.json编译 总结 muduo源码编译安装 muduo依赖Boost库&#xff0c;所以我们应…...

C语言笔记10

1.用指针打印一维数组 //1.用指针打印一维数组 #include <stdio.h> int main() {//int arr[] { 1,2,3,4,5 };int arr[5] { 0 };int* p &arr[0];int sz sizeof(arr) / sizeof(arr[0]);for (int i 0; i < sz; i){scanf("%d", &arr[i]);}//int* …...

BMS-HiL主要功能

BMS HIL 系统中 PC 机中安装实验管理软件用于测试过程管理和测试序列编辑&#xff0c;通过以太网与 PXI 机箱中的处理器进行连接&#xff0c;处理器中运行实时系统&#xff08;Real Time&#xff09;并安装 Veristand 终端引擎&#xff0c;通过与 PC 中的数据传输&#xff0c;对…...

idea无法识别加载pom.xml文件

有时idea无法识别加载pom.xml文件&#xff0c;直接打开pom.xml文件&#xff0c;然后添加到maven就行...

C语言笔记12

1.使用qsort函数进行排序 #include <stdio.h> #include <stdlib.h> #include <string.h>//void qsort(void* base, //指向待排序数组的第一个元素的指针 // size_t num, //base指向数组中的元素个数 // size_t size,//base指向的数组中一…...

说一下 hibernate 的缓存机制?

Hibernate 的缓存机制是为了提高应用程序的性能&#xff0c;通过减少对数据库物理数据源的访问频次而设计的。Hibernate 的缓存主要可以分为两个级别&#xff1a;一级缓存&#xff08;也称为 Session 级别的缓存&#xff09;和二级缓存&#xff08;也称为 SessionFactory 级别的…...

Mac安装jadx

1、使用命令brew安装 : brew install jadx 输入完命令,等待安装完毕 备注&#xff08;关于Homebrew &#xff09;&#xff1a; Homebrew 是 MacOS 下的包管理工具&#xff0c;类似 apt-get/apt 之于 Linux&#xff0c;yum 之于 CentOS。如果一款软件发布时支持了 homebrew 安…...

风扇开启执行逻辑

执行流程 public static void businessExecutionWork(){//以下为业务逻辑部分System.out.println("1、根据电池包控制风扇服务执行 开始!");//1、获取电池包电压、电流、环境温度//获取电池包电压、电流、环境温度ObtainBatteryDataService obtainBatteryDataServic…...

调用函数实现数组的输入排序插入与输出

输入一串数字&#xff0c;输出排序插入后输出最后的数字序列&#xff1b; 把主要步骤交给主函数&#xff0c;通过调用函数来实现整体的功能&#xff1a; 写出主函数&#xff08;这里使用冒泡排序&#xff09;&#xff1a; int main(){int n;int a[10];cin>>n;inputa(a…...

代码随想录算法训练营Day 41| 动态规划part03 | 343. 整数拆分、96.不同的二叉搜索树

代码随想录算法训练营Day 41| 动态规划part03 | 343. 整数拆分、96.不同的二叉搜索树 文章目录 代码随想录算法训练营Day 41| 动态规划part03 | 343. 整数拆分、96.不同的二叉搜索树343. 整数拆分一、动态规划二、贪心&#xff08;不需要掌握&#xff09; 96.不同的二叉搜索树一…...

多模态产品在智能文档处理应用的展望------以TextIn模型为例

前言发展现状TextIn 文档解析技术文本向量化展望合合信息 前言 第十四届视觉与学习青年学者研讨会(VALSE 2024)于5月5日-7日在山城重庆渝北区悦来国际会议中心举办。大会聚焦计算机视觉、模式识别、多媒体和机器学习等领域的国际前沿和热点方向。大会中&#xff0c;合合信息智能…...

上海市计算机学会竞赛平台2024年3月月赛丙组最近的数字

题目描述 给定两个正整数 &#x1d45b;n 与 &#x1d451;d &#xff0c;请找到所有最接近 &#x1d45b;n 且是 &#x1d451;d 的倍数的整数。 输入格式 第一行&#xff1a;单个整数表示 &#x1d45b;n第二行&#xff1a;单个整数表示 &#x1d451;d 输出格式 若干行…...

RFID在汽车制造中的应用如何改变行业

随着工业4.0和中国制造2025的推进&#xff0c;企业对于智能化、自动化的需求日益增长&#xff0c;RFID射频技术在制造业中已经相当普遍了。在如今这瞬息万变的行业与时代中&#xff0c;RFID技术可以帮助企业获得竞争优势&#xff0c;简化日益复杂的生产流程&#xff0c;推动企业…...

sCrypt受邀在中国人民大学举办《区块链与数字经济》课程讲座

4月17日&#xff0c;可一科技特邀美国sCrypt公司的开发工程师周全&#xff0c;在中国人民大学的《区块链与数字经济》课程上进行了讲座。周全讲解了区块链的分布式设计、不可篡改特性&#xff0c;以及智能合约的基本原理&#xff0c;利用“智能家居触发机制”等生动比喻&#x…...

pc端的鼠标箭头变换

<div style"cursor:pointer"></div>...

ICode国际青少年编程竞赛- Python-2级训练场-for循环练习2

ICode国际青少年编程竞赛- Python-2级训练场-for循环练习2 1、 for i in range(5):Dev.step(9 - i * 2)Dev.turnLeft()2、 for i in range(3):Spaceship.step(i 1)Spaceship.turnRight()Spaceship.step(i 1)Spaceship.turnLeft()3、 for i in range(4):Dev.step(10 - i…...

RiPro主题美化【支付弹窗底部提示语根据入口不同有不同的提示】ritheme主题美化RiProV2 增加支付提示语,按支付类型不同,入口不同提示语不同的设置

RiPro主题美化【支付弹窗底部提示语根据入口不同有不同的提示】ritheme主题美化RiProV2 增加支付提示语,按支付类型不同,入口不同提示语不同的设置 背景: 接上文:https://www.uu2id.com/827.html 付费组件在以下几个地方会弹出:1)文章隐藏内容付费;2)付费资源下载;3…...

MSMQ消息队列

MQ是一种企业服务的消息中间节技术&#xff0c;这种技术常常伴随着企业服务总线相互使用&#xff0c;构成了企业分布式开发的一部分&#xff0c;如果考虑到消息的发送和传送之间是可以相互不联系的并且需要分布式架构&#xff0c;则可以考虑使用MQ做消息的中间价技术&#xff0…...

树莓派nmap扫描

debian系统安装nmap&#xff1a; sudo apt install nmap安装nmap完成后&#xff0c;输入 ip route 来查看当前Wi-Fi路由器的ip地址。 第一行的default via后显示的便是网关地址&#xff0c;也就是路由器地址。 获取到路由器ip地址后&#xff0c;在终端中输入&#xff1a; …...

【必看】Spring系列面试题

Spring Core Container, AOP, Data Access, Web... 基础 1. 简单介绍Spring 一款开源的轻量级 Java 开发框架&#xff0c;旨在提高开发人员的开发效率以及系统的可维护性。Spring 支持 IoC&#xff08;Inversion of Control:控制反转&#xff09; 和 AOP(Aspect-Oriented Pro…...

浏览器访问 AWS ECS 上部署的 Docker 容器(监听 80 端口)

✅ 一、ECS 服务配置 Dockerfile 确保监听 80 端口 EXPOSE 80 CMD ["nginx", "-g", "daemon off;"]或 EXPOSE 80 CMD ["python3", "-m", "http.server", "80"]任务定义&#xff08;Task Definition&…...

VB.net复制Ntag213卡写入UID

本示例使用的发卡器&#xff1a;https://item.taobao.com/item.htm?ftt&id615391857885 一、读取旧Ntag卡的UID和数据 Private Sub Button15_Click(sender As Object, e As EventArgs) Handles Button15.Click轻松读卡技术支持:网站:Dim i, j As IntegerDim cardidhex, …...

Appium+python自动化(十六)- ADB命令

简介 Android 调试桥(adb)是多种用途的工具&#xff0c;该工具可以帮助你你管理设备或模拟器 的状态。 adb ( Android Debug Bridge)是一个通用命令行工具&#xff0c;其允许您与模拟器实例或连接的 Android 设备进行通信。它可为各种设备操作提供便利&#xff0c;如安装和调试…...

sqlserver 根据指定字符 解析拼接字符串

DECLARE LotNo NVARCHAR(50)A,B,C DECLARE xml XML ( SELECT <x> REPLACE(LotNo, ,, </x><x>) </x> ) DECLARE ErrorCode NVARCHAR(50) -- 提取 XML 中的值 SELECT value x.value(., VARCHAR(MAX))…...

Ascend NPU上适配Step-Audio模型

1 概述 1.1 简述 Step-Audio 是业界首个集语音理解与生成控制一体化的产品级开源实时语音对话系统&#xff0c;支持多语言对话&#xff08;如 中文&#xff0c;英文&#xff0c;日语&#xff09;&#xff0c;语音情感&#xff08;如 开心&#xff0c;悲伤&#xff09;&#x…...

均衡后的SNRSINR

本文主要摘自参考文献中的前两篇&#xff0c;相关文献中经常会出现MIMO检测后的SINR不过一直没有找到相关数学推到过程&#xff0c;其中文献[1]中给出了相关原理在此仅做记录。 1. 系统模型 复信道模型 n t n_t nt​ 根发送天线&#xff0c; n r n_r nr​ 根接收天线的 MIMO 系…...

大语言模型(LLM)中的KV缓存压缩与动态稀疏注意力机制设计

随着大语言模型&#xff08;LLM&#xff09;参数规模的增长&#xff0c;推理阶段的内存占用和计算复杂度成为核心挑战。传统注意力机制的计算复杂度随序列长度呈二次方增长&#xff0c;而KV缓存的内存消耗可能高达数十GB&#xff08;例如Llama2-7B处理100K token时需50GB内存&a…...

GruntJS-前端自动化任务运行器从入门到实战

Grunt 完全指南&#xff1a;从入门到实战 一、Grunt 是什么&#xff1f; Grunt是一个基于 Node.js 的前端自动化任务运行器&#xff0c;主要用于自动化执行项目开发中重复性高的任务&#xff0c;例如文件压缩、代码编译、语法检查、单元测试、文件合并等。通过配置简洁的任务…...

基于Springboot+Vue的办公管理系统

角色&#xff1a; 管理员、员工 技术&#xff1a; 后端: SpringBoot, Vue2, MySQL, Mybatis-Plus 前端: Vue2, Element-UI, Axios, Echarts, Vue-Router 核心功能&#xff1a; 该办公管理系统是一个综合性的企业内部管理平台&#xff0c;旨在提升企业运营效率和员工管理水…...

零知开源——STM32F103RBT6驱动 ICM20948 九轴传感器及 vofa + 上位机可视化教程

STM32F1 本教程使用零知标准板&#xff08;STM32F103RBT6&#xff09;通过I2C驱动ICM20948九轴传感器&#xff0c;实现姿态解算&#xff0c;并通过串口将数据实时发送至VOFA上位机进行3D可视化。代码基于开源库修改优化&#xff0c;适合嵌入式及物联网开发者。在基础驱动上新增…...