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

news2024/12/22 19:34:35

文章目录

  • 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+Port
            const 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; //#1
    EventLoop *_loop;  //#2 把它看作epoll

}

int main () {
    EventLoop loop; //epoll
    InetAddress addr("127.0.0.1", 10000);
    ChatServer server(&loop, addr, "ChatServer");

    server.start(); //listenfd epollctl=>epoll
    loop.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复用的高并发高性能服务器。

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

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

相关文章

智能BI产品设计

BI概念 目录 BI概念 一&#xff1a;与BI相关的几个重要概念 二&#xff1a;数据仓库 VS 数据库 BI架构 一&#xff1a;数据分析通用流程 二&#xff1a;BI平台基本架构 可视化图形 一&#xff1a;如何选择可视化图形 二&#xff1a;数据展示形式 三&#xff1a;数据…

【driver3】proc文件系统,内存分配,数据类型/移植/对齐,内核中断,通过IO内存访问外设,PCI

文章目录 1.创建proc文件系统接口&#xff1a;之前调试内核时都是通过prink打印内核信息&#xff0c;通过dmesg查看输出的信息。新调试方法&#xff1a;利用proc文件系统在pro文件夹下创建接口&#xff0c;读写这个接口就可实现对内核的调试2.内核内存分配函数&#xff1a;top&…

spring ioc 容器加载过程 refresh() 方法详解

IOC 加载过程 从 new ClassPathXmlApplicationContext开始 ApplicationContext context new ClassPathXmlApplicationContext("classpath:application.xml");ClassPathXmlApplicationContext类构造方法 public ClassPathXmlApplicationContext(String[] configLo…

自动驾驶融合定位:IMU内参模型及标定

自动驾驶融合定位&#xff1a;IMU内参模型及标定 一、 概述 标定的本质是参数辨识。首先明确哪些参数可辨识&#xff0c;其次弄清怎样辨识。 参数包括陀螺仪和加速度计各自的零偏、标度因数、安装误差。 辨识就比较丰富了&#xff0c;如果让各位先不局限于标定任务&#xf…

数据分析——业务指标分析

业务指标分析 前言一、业务指标分析的定义二、业务问题构建问题构建的要求 三、业务问题的识别在识别问题的阶段对于企业内部收益者的补充 四、竞争者分析竞争者分析的内容竞争者分析目的案例 五、市场机会识别好的市场机会必须满足的条件市场机会案例 六、风险控制数据分析师常…

【Git实战】如何将本地仓库推送至Github(windows版)?

最近使用Go语言开发项目&#xff0c;想寻找位操作相关的工具包。找了一圈没有合适的&#xff0c;因此自己写了一个。又想将其推送到Github上&#xff0c;中间逢山开路&#xff0c;遇水搭桥&#xff0c;终于成功将本地Git仓库和Github进行了关联。现将我的方法公之于众&#xff…

网页html版面分析-- BeauifulSoup(python 文档解析提取)

介绍 BeauifulSoup 是一个可以从HTML或XML 文件中提取数据的python库&#xff1b;它能通过转换器实现惯用的文档导航、查找、修改文档的方式。 BeauifulSoup是一个基于re开发的解析库&#xff0c;可以提供一些强大的解析功能&#xff1b;使用BeauifulSoup 能够提高提取数据的效…

国产最强多模态大模型Step Fun-1V,究竟有多好用?

前言&#xff1a; 2023年是大模型的元年&#xff0c;随着ChatGPT的爆火&#xff0c;将大模型带入了公众的视野 &#xff0c;国内也随之掀起了百模大战的浪潮。在这股浪潮的推动下&#xff0c;诸多科技公司&#xff0c;乃至大厂都争相推出自己的大模型产品&#xff0c;希望能够…

华为 二层交换机与防火墙连通上网实验

防火墙是一种网络安全设备&#xff0c;用于监控和控制网络流量。它可以帮助防止未经授权的访问&#xff0c;保护网络免受攻击和恶意软件感染。防火墙可以根据预定义的规则过滤流量&#xff0c;例如允许或阻止特定IP地址或端口的流量。它也可以检测和阻止恶意软件、病毒和其他威…

element-plus upload查看图片后不展示press delete to remove?

直接上全局样式就行&#xff1a; :deep(.el-upload-list__item .el-icon--close-tip) {display: none !important; }

灌溉机器人 状压dp

灌溉机器人 题目描述 农田灌溉是一项十分费体力的农活&#xff0c;特别是大型的农田。小明想为农民伯伯们减轻农作负担&#xff0c;最近在研究一款高科技——灌溉机器人。它可以在远程电脑控制下&#xff0c;给农田里的作物进行灌溉。 现在有一片 N 行 M 列的农田。农田的土…

【数据结构】有关环形链表题目的总结

文章目录 引入 - 快慢指针思考 - 快慢指针行走步数进阶 - 寻找环形链表的头 引入 - 快慢指针 141-环形链表 - Leetcode 关于这道题&#xff0c;大家可以利用快慢指针&#xff0c;一个每次走两步&#xff0c;一个每次走一步&#xff0c;只要他们有一次相撞了就代表说这是一个链…

【吃透Java手写】SpringBoot-简易版-源码解析

【吃透Java手写】SpringBoot-简易版-源码解析 1 SpringbootDemo2 准备工作2.1 Springboot-my2.1.1 依赖2.1.2 SpringBootApplication2.1.3 SJBSpringApplication2.1.3.1 run方法 2.2 Springboot-user2.2.1 依赖2.2.2 UserController2.2.3 UserApplication 2.3 分析run方法的逻辑…

13 【PS作图】人物绘画理论-脸型

三庭五眼 三庭&#xff1a;脸的长度比例 &#xff08;1&#xff09;发际线到眉毛 &#xff08;2&#xff09;眉毛到鼻底 &#xff08;3&#xff09;鼻底到下巴 三个部分大致为三等分 五眼&#xff1a;脸的宽度比例 以眼睛长度为单位&#xff0c;把脸的宽度分成五等分&#x…

为什么很多人不推荐你用JWT?

为什么很多人不推荐你用JWT? 如果你经常看一些网上的带你做项目的教程&#xff0c;你就会发现 有很多的项目都用到了JWT。那么他到底安全吗&#xff1f;为什么那么多人不推荐你去使用。这个文章将会从全方面的带你了解JWT 以及他的优缺点。 什么是JWT? 这个是他的官网JSON…

搜索算法系列之四(斐波那契)

以下算法被验证过&#xff0c;如有什么问题或有补充的欢迎留言。 前言 斐波那契数列&#xff0c;又称黄金分割数列&#xff0c;是由意大利数学家&#xff08;Leonardo Fibonacci&#xff09;在1202年提出的。这个数列的递推关系是F(0)1&#xff0c;F(1)1&#xff0c;F(n)F(n-…

微搭低代码入门05文件的上传和下载

目录 1 创建数据源2 创建应用3 创建页面4 设置导航功能5 文件上传6 文件下载总结 小程序中&#xff0c;我们通常会有文件的上传和下载的需&#xff0c;在微搭中&#xff0c;文件是存放在云存储中&#xff0c;每一个文件都会有一个唯一的fileid&#xff0c;我们本篇就介绍如何通…

强化学习玩flappy_bird

强化学习玩flappy_bird&#xff08;代码解析&#xff09; 游戏地址&#xff1a;https://flappybird.io/ 该游戏的规则是&#xff1a; 点击屏幕则小鸟立即获得向上速度。 不点击屏幕则小鸟受重力加速度影响逐渐掉落。 小鸟碰到地面会死亡&#xff0c;碰到水管会死亡。&#…

vscode连接服务器的docker步骤

进入容器之后&#xff0c;操作方式与本地windows系统操作逻辑一样&#xff1b;容器内部结构都能任意查看和使用&#xff0c;创建文件及编写python脚本都可以直接使用vs code编辑器进行编辑和调试&#xff0c;从而避免使用命令行及vim编辑文件&#xff0c;非常直观且方便~

4. RedHat认证-进程管理

4. RedHat认证-进程管理 1.进程概念 进程就是正在运行中的程序或者命令 每一个进程都是运行的实体&#xff0c;都有自己的地址空间&#xff0c;并占有一定的资源空间 程序消耗的是磁盘资源、进程消耗的是内存和CPU资源 进程会占用四类资源&#xff08;CPU 、内存、磁盘、网…