RabbitMQ的学习和模拟实现|muduo库的介绍和使用

muduo库

项目仓库:https://github.com/ffengc/HareMQ

  • muduo库
    • muduo库是什么
    • 快速上手搭建服务端
    • 快速上手搭建客户端
    • 上面搭建的服务端-客户端通信还有什么问题?
    • muduo库中的protobuf
    • 基于muduo库中的protobuf协议实现一个服务器

muduo库是什么

Muduo由陈硕大佬开发,是一个基于非阻塞10和事件驱动的C++高并发TCP网络编程库。 它是一款基于主从Reactor模型的网络库,其使用的线程模型是oneloop perthread,所谓one loop per thread指的是:

  • 一个线程只能有一个事件循环(EventLoop),用于响应计时器和IO事件。
  • 一个文件米哦啊舒服只能由一个线程进行读写,换句话说,就是一个TCP链接必须归属于某个EventLoop管理

我对reactor模式在项目中也是有详细的描述的,这里不再重复

  • Reactor模式: ffengc/Reactor-based-HyperWebServer
  • 事件驱动: ffengc/Event-Driven-Pipeline-Communication-System-Framework

快速上手搭建服务端

简单写一个英译汉服务器和客户端,快速上手Muduo库。

要先把东西准备好:第三方库如果没有像protobuf一样安装到系统目录下了,就统一放到HareMQ/libs里面去就行了。

muduo中我们需要的是build/release-install-cpp11/里面的includelib

这样包含就行了:

#ifndef __YUFC_DEMO_DICT_SERVER_USE_MUDUO__
#define __YUFC_DEMO_DICT_SERVER_USE_MUDUO__#include "../../libs/muduo/include/muduo/net/TcpServer.h"
#include "../../libs/muduo/include/muduo/net/EventLoop.h"
#include "../../libs/muduo/include/muduo/net/TcpConnection.h"#endif

直接写一个简单服务作为例子:

#ifndef __YUFC_DEMO_DICT_SERVER_USE_MUDUO__
#define __YUFC_DEMO_DICT_SERVER_USE_MUDUO__#include "../../libs/muduo/include/muduo/net/EventLoop.h"
#include "../../libs/muduo/include/muduo/net/TcpConnection.h"
#include "../../libs/muduo/include/muduo/net/TcpServer.h"
#include "../log.hpp"class translate_server {
private:muduo::net::EventLoop __base_loop; // 基本的事件循环(这个要传给server, 所以要放前面)muduo::net::TcpServer __server; // 服务器对象
private:// 新连接建立成功时的回调函数// 会在一个连接建立成功,以及关闭的时候被调用void onConnection(const muduo::net::TcpConnectionPtr& conn) {if (conn->connected() == true)LOG(INFO) << "new connection!" << std::endl;elseLOG(INFO) << "connection close" << std::endl;}// 通信连接收到请求时的回调函数void onMessage(const muduo::net::TcpConnectionPtr& conn, muduo::net::Buffer* buf, muduo::Timestamp) {std::string str = buf->retrieveAllAsString();std::string resp = translate(str);conn->send(resp); // 向客户端进行发送}std::string translate(const std::string& str) {// 用个简单例子就行static std::unordered_map<std::string, std::string> __dict_map = {{ "hello", "nihao" }, { "nihao", "hello" }};auto it = __dict_map.find(str);if (it == __dict_map.end())return "unknown";return it->second;}public:translate_server(int port): __server(&__base_loop,muduo::net::InetAddress("0.0.0.0", port),"translate_server",muduo::net::TcpServer::kReusePort) {__server.setConnectionCallback(std::bind(&translate_server::onConnection,this, std::placeholders::_1)); // 设置回调__server.setMessageCallback(std::bind(&translate_server::onMessage,this, std::placeholders::_1,std::placeholders::_2,std::placeholders::_3)); // 设置回调}void start() {__server.start(); // 开始事件监听__base_loop.loop(); // 开始事件监控,这是一个死循环阻塞接口}
};#endifint main() {translate_server server(8085);server.start();return 0;
}

这个例子很简单,不作过多描述,维护好事件loop对象和服务器对象即可。

makefile‼️

server: dict_server.ccg++ -o $@ $^ -std=c++11 -I../../libs/muduo/include -L../../libs/muduo/lib -lmuduo_net -lmuduo_base -lpthread
.PHONY:clean
clean:rm -f server

[!CAUTION]
这里要注意,用-L选项来指定库的地址。
Linux上静态库库的名称需要去掉头上的lib和后面的.a,具体为什么可以另外去了解linux库的机制
-I指定头文件路径‼️然后cc文件里面的头文件路径就可以相应修改了

服务器小例子完整代码

#ifndef __YUFC_DEMO_DICT_SERVER_USE_MUDUO__
#define __YUFC_DEMO_DICT_SERVER_USE_MUDUO__// #include "../../libs/muduo/include/muduo/net/EventLoop.h"
// #include "../../libs/muduo/include/muduo/net/TcpConnection.h"
// #include "../../libs/muduo/include/muduo/net/TcpServer.h"
#include "../log.hpp"
#include "muduo/net/EventLoop.h"
#include "muduo/net/TcpConnection.h" // 因为makefile指定了头文件路径,所以这里直接这样用
#include "muduo/net/TcpServer.h"
#include <unordered_map>class translate_server {
private:muduo::net::EventLoop __base_loop; // 基本的事件循环(这个要传给server, 所以要放前面)muduo::net::TcpServer __server; // 服务器对象
private:// 新连接建立成功时的回调函数// 会在一个连接建立成功,以及关闭的时候被调用void onConnection(const muduo::net::TcpConnectionPtr& conn) {if (conn->connected() == true)LOG(INFO) << "new connection!" << std::endl;elseLOG(INFO) << "connection close" << std::endl;}// 通信连接收到请求时的回调函数void onMessage(const muduo::net::TcpConnectionPtr& conn, muduo::net::Buffer* buf, muduo::Timestamp) {std::string str = buf->retrieveAllAsString();LOG(INFO) << "recv a mesg: " << str << std::endl;std::string resp = translate(str);conn->send(resp); // 向客户端进行发送}std::string translate(const std::string& str) {// 用个简单例子就行static std::unordered_map<std::string, std::string> __dict_map = {{ "hello", "nihao" }, { "nihao", "hello" }};auto it = __dict_map.find(str); // 这里的str包含了\n,需要额外处理,不过这里只是为了学习使用服务器,不处理了if (it == __dict_map.end())return "unknown\n";return it->second;}public:translate_server(int port): __server(&__base_loop,muduo::net::InetAddress("0.0.0.0", port),"translate_server",muduo::net::TcpServer::kReusePort) {__server.setConnectionCallback(std::bind(&translate_server::onConnection,this, std::placeholders::_1)); // 设置回调__server.setMessageCallback(std::bind(&translate_server::onMessage,this, std::placeholders::_1,std::placeholders::_2,std::placeholders::_3)); // 设置回调}void start() {__server.start(); // 开始事件监听__base_loop.loop(); // 开始事件监控,这是一个死循环阻塞接口}
};#endifint main() {translate_server server(8085);server.start();return 0;
}

[!WARNING]
注意:因为我们对发送的字符串没有做处理,我们只是学习muduo的使用方法,所以发送hello其实是发送hello\n,所以服务器返回一直是unknown,这个我也不进行处理了,知道这个原因即可,我们的重点不在这。

快速上手搭建客户端

和服务端基本是一样的,但是需要注意:

[!CAUTION]
客户端不能完全非阻塞,客户端建立连接一定要成功才能继续往下走的
但是因为muduo库里面所以操作都是异步非阻塞的
因此需要使用CountDownLatch组件来维持同步‼️

客户端完整代码如下所示。

#ifndef __YUFC_DEMO_DICT_CLIENT_USE_MUDUO__
#define __YUFC_DEMO_DICT_CLIENT_USE_MUDUO__#include "../log.hpp"
#include "muduo/base/CountDownLatch.h"
#include "muduo/net/EventLoopThread.h"
#include "muduo/net/TcpClient.h"
#include "muduo/net/TcpConnection.h"/* 注意,客户端连接服务器是需要阻塞等待连接建立成功之后才返回的,所以才需要使用 CountDownLatch */class translate_client {
private:muduo::CountDownLatch __latch;muduo::net::EventLoopThread __loop_thread;muduo::net::TcpClient __client;muduo::net::TcpConnectionPtr __conn;private:// 连接成功的回调void onConnection(const muduo::net::TcpConnectionPtr& conn) {if (conn->connected()) {// 如果连接建立成功了,就计数器--__latch.countDown();LOG(INFO) << "connection to server success" << std::endl;__conn = conn; // 保存这个连接} else {// 连接关闭LOG(INFO) << "connection to server end" << std::endl;__conn.reset(); // 清空}}// 收到服务器发来的消息的回调void onMessage(const muduo::net::TcpConnectionPtr& conn, muduo::net::Buffer* buf, muduo::Timestamp) {std::cout << "server# " << buf->retrieveAllAsString() << std::endl;}public:translate_client(const std::string& sip, int sport): __latch(1), __client(__loop_thread.startLoop(),muduo::net::InetAddress(sip, sport),"translate_client") {__client.setConnectionCallback(std::bind(&translate_client::onConnection,this, std::placeholders::_1));__client.setMessageCallback(std::bind(&translate_client::onMessage,this, std::placeholders::_1,std::placeholders::_2,std::placeholders::_3));}void connect() {__client.connect(); // 这里是立即返回的,但是我们需要控制阻塞等待!__latch.wait();}bool send(const std::string& mesg) {// 因为muduo里面的所有操作都是异步的,不知道什么时候可能连接都关闭了,所以是要判断的if (__conn->connected()) {__conn->send(mesg);return true;}return false;}
};#endifint main() {translate_client client("127.0.0.1", 8085);client.connect();while (1) {std::string buf;std::cin >> buf;client.send(buf);}return 0;
}

makefile:

.PHONY:all
all: server clientserver: dict_server.ccg++ -o $@ $^ -std=c++11 -I../../libs/muduo/include -L../../libs/muduo/lib -lmuduo_net -lmuduo_base -lpthread
client: dict_client.ccg++ -o $@ $^ -std=c++11 -I../../libs/muduo/include -L../../libs/muduo/lib -lmuduo_net -lmuduo_base -lpthread
.PHONY:clean
clean:rm -f server client

现在我们就可以用自己的客户端了,不用telnet了。

[!WARNING]
注意:刚才使用telnet,因为我们对发送的字符串没有做处理,我们只是学习muduo的使用方法,所以发送hello其实是发送hello\n,所以服务器返回一直是unknown,这个我也不进行处理了,知道这个原因即可,我们的重点不在这。
但是在这里,我们使用的muduo库会办我们处理这个\n,因此我们可以得到正确的结果。

上面搭建的服务端-客户端通信还有什么问题?

问题非常大,也是老生常谈的问题了,没有处理粘包的问题,没有协议。

因此后面我们就要利用protobuf,结合muduo库,来简单实现一个服务器的demo。

muduo库中的protobuf

muduo库已经给我们写好基于protobuf的网络通信协议框架了,已经写好了。

路径: muduo/examples/protobuf/codec里面。

调用流程如图所示:

了解了上述关系,接下来就可以通过muduo库中陈硕大佬提供的接口来编写我们的客户端/服务器端通信了,其最为简便之处就在于我们可以把更多的精力放到业务处理函数的实现上,而不是服务器的搭建或者协议的解析处理上了。

基于muduo库中的protobuf协议实现一个服务器

基于muduo库中,对于protobuf协议的处理代码,实现一个翻译+加法服务器与客户端。

  1. 编写proto文件,生成相关结构代码
  2. 编写服务端代码,搭建服务器
  3. 编写客户端代码,搭建客户端

定义proto文件。

syntax = "proto3";package yufc;message translateRequest {string msg = 1;
};message translateResponse {string msg = 1;
};message addRequest {int32 num1 = 1;int32 num2 = 2;
};message addResponse {int32 result = 1;
};

如图所示生成proto文件。

接下来我们要准备好头文件,是在example里面的。

我们要把muduo/examples/protobuf/codec下的dispatcher.h, codec.cccodec.h放到我们整理好的第三方库的地方去。

[!NOTE]
这里可以把codec.cc里面的方法复制到codec.h里面,这样直接调用头文件即可
如果不这样操作,等下编译的时候记得把codec.cc也进行编译

[!CAUTION]
注意:如果没做上面这一步复制,codec.cc里面原来包含的头文件路径是#include "examples/protobuf/codec/codec.h"
这个是错误的,现在的codec.h就在codec.cc同级目录下,所以应该直接修改成 #include "codec.h"
此外,不同版本可能还会出现其他问题,头文件缺失等,需要自行去源代码中寻找,然后放到相应位置即可

具体服务端客户端代码如何写,可以见我的demo代码,其实就是参照muduo/examples/protobuf/codec下面的例子去写的!

更新后的 makefile

这过程中遇到了许多问题,大家要记得链接库等这些细节了。

server: proto_server.cc request.pb.cc /home/parallels/Project/HareMQ/HareMQ/libs/muduo/include/muduo/protoc/codec.ccg++ -o $@ $^ -std=c++11 -I../../libs/muduo/include -L../../libs/muduo/lib -lmuduo_net -lmuduo_base -lpthread -lprotobuf -lz
client: proto_client.cc request.pb.cc /home/parallels/Project/HareMQ/HareMQ/libs/muduo/include/muduo/protoc/codec.ccg++ -o $@ $^ -std=c++11 -I../../libs/muduo/include -L../../libs/muduo/lib -lmuduo_net -lmuduo_base -lpthread -lprotobuf -lz
.PHONY:clean
clean:rm -f server client

这样就是测试成功了。

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

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

相关文章

ReadAgent,一款具有要点记忆的人工智能阅读代理

人工智能咨询培训老师叶梓 转载标明出处 现有的大模型&#xff08;LLMs&#xff09;在处理长文本时受限于固定的最大上下文长度&#xff0c;并且当输入文本越来越长时&#xff0c;性能往往会下降&#xff0c;即使在没有超出明确上下文窗口的情况下&#xff0c;LLMs 的性能也会随…

pytorch 笔记:torch.optim.Adam

torch.optim.Adam 是一个实现 Adam 优化算法的类。Adam 是一个常用的梯度下降优化方法&#xff0c;特别适合处理大规模数据集和参数的深度学习模型 torch.optim.Adam(params, lr0.001, betas(0.9, 0.999), eps1e-08, weight_decay0, amsgradFalse, *, foreachNone, maximizeFa…

OpenAI从GPT-4V到GPT-4O,再到GPT-4OMini简介

OpenAI从GPT-4V到GPT-4O&#xff0c;再到GPT-4OMini简介 一、引言 在人工智能领域&#xff0c;OpenAI的GPT系列模型一直是自然语言处理的标杆。随着技术的不断进步&#xff0c;OpenAI推出了多个版本的GPT模型&#xff0c;包括视觉增强的GPT-4V&#xff08;GPT-4 with Vision&…

HarmonyOS应用开发者高级认证,Next版本发布后最新题库 - 单选题序号3

基础认证题库请移步&#xff1a;HarmonyOS应用开发者基础认证题库 注&#xff1a;有读者反馈&#xff0c;题库的代码块比较多&#xff0c;打开文章时会卡死。所以笔者将题库拆分&#xff0c;单选题20个为一组&#xff0c;多选题10个为一组&#xff0c;题库目录如下&#xff0c;…

hive3 hql脚本传递参数

在数仓的构建过程中&#xff0c;需要配置hive的调度任务&#xff0c;这时就需要对hive hql脚本进行封装&#xff0c;将参数提取出来&#xff0c;作为变量进行配置&#xff0c;比如日期、类型等。 hive3版本&#xff0c;hive -f 在执行sql脚本文件的时候是可以传递参数。 具体…

GitHub 令牌泄漏, Python 核心资源库面临潜在攻击

TheHackerNews网站消息&#xff0c;软件供应链安全公司 JFrog 的网络安全研究人员称&#xff0c;他们发现了一个意外泄露的 GitHub 令牌&#xff0c;可授予 Python 语言 GitHub 存储库、Python 软件包索引&#xff08;PyPI&#xff09;和 Python 软件基金会&#xff08;PSF&…

系统架构设计师教程 第3章 信息系统基础知识-3.5 专家系统-解读

系统架构设计师教程 第3章 信息系统基础知识-3.5 专家系统(ES) 3.5.1 人工智能3.5.1.1 人工智能的特点3.5.1.2 人工智能的主要分支3.5.2 ES的概念3.5.2.1 ES 概述3.5.2.2 与传统程序的区别3.5.3 ES的特点3.5.4 ES的组成3.5.4.1 知识库3.5.4.2 综合数据库3.5.4.3 推理机3.5.4.…

google 浏览器插件开发简单学习案例:TodoList

参考&#xff1a; google插件支持&#xff1a; https://blog.csdn.net/weixin_42357472/article/details/140412993 这里是把前面做的TodoList做成google插件&#xff0c;具体网页可以参考下面链接 TodoList网页&#xff1a; https://blog.csdn.net/weixin_42357472/article/de…

MongoDB教程(十八):MongoDB MapReduce

&#x1f49d;&#x1f49d;&#x1f49d;首先&#xff0c;欢迎各位来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里不仅可以有所收获&#xff0c;同时也能感受到一份轻松欢乐的氛围&#xff0c;祝你生活愉快&#xff01; 文章目录 引言一、MapRed…

OpenCV 图像旋转和平移 数学和代码原理详解

文章目录 数学原理旋转矩阵平移和旋转合成变换矩阵应用在OpenCV中的实现 代码关键点解读完整代码C代码&#xff1a;Python代码&#xff1a; 在OpenCV中进行图像旋转涉及到一些基本的几何变换和图像处理操作。 数学原理 在图像旋转中&#xff0c;背后的数学原理主要涉及二维欧…

阿里云ubuntu宝塔面板部署uni-app-flask-websocket前后端项目

1.下载宝塔面板 wget -O install.sh https://download.bt.cn/install/install-ubuntu_6.0.sh && sudo bash install.sh ed8484bec 然后去安全组开放对应的端口 面板账户登录信息 【云服务器】请在安全组放行 29725 端口 进入控制面板后修改默认用户名和密码 2. …

linux、windows、macos,命令终端清屏

文章目录 LinuxWindowsmacOS 在Linux、Windows和macOS的命令终端中&#xff0c;清屏的命令或方法各不相同。以下是针对这三种系统的清屏方法&#xff1a; Linux clear命令&#xff1a;这是最常用的清空终端屏幕的命令之一。在终端中输入clear命令后&#xff0c;屏幕上的所有内容…

Web开发:ASP.NET CORE使用Ajax定时获取后端数据

一、低难度&#xff08;刷新a标签&#xff09; 1、需求 给a标签每15s刷新一次&#xff0c;显示最新的时间&#xff08;时间必须由后端获取&#xff09; 应该如何操作呢 2、代码 后端 using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; using Mi…

微信小程序获取蓝牙并实现内容打印

通过微信小程序如何实现获取蓝牙打印机并实现打印能力&#xff0c;之前做过一个测试Dome&#xff0c;能够获取附近的蓝牙打印机设备并实现打印,今天开放出来供大家参考。 wxml <!--右下角搜索--> <view class"ly-cass-box"><view class"ly-cas…

Docker核心技术:Docker原理之Namespace

云原生学习路线导航页&#xff08;持续更新中&#xff09; 本文是 Docker核心技术 系列文章&#xff1a;Docker原理之Namespace&#xff0c;其他文章快捷链接如下&#xff1a; 应用架构演进容器技术要解决哪些问题Docker的基本使用Docker是如何实现的 Docker核心技术&#xff1…

设计模式思想

设计模式思想 1. 理论2. 结构型模式——更优雅的声明和构建对象2.1 适配器模式——将一个类的接口适配成用户所期待的类2.2 代理模式——创建一个代理对象劫持对目标对象的调用&#xff0c;为目标访问增加一层控制和拦截&#xff0c;将控制逻辑和主逻辑隔离2.3 装饰器模式——在…

017、Vue动态tag标签

文章目录 1、先看效果2、代码 1、先看效果 2、代码 <template><div class "tags"><el-tag size"medium"closable v-for"item,index in tags":key"item.path":effect"item.title$route.name?dark:plain"cl…

【中项】系统集成项目管理工程师-第4章 信息系统架构-4.3应用架构

前言&#xff1a;系统集成项目管理工程师专业&#xff0c;现分享一些教材知识点。觉得文章还不错的喜欢点赞收藏的同时帮忙点点关注。 软考同样是国家人社部和工信部组织的国家级考试&#xff0c;全称为“全国计算机与软件专业技术资格&#xff08;水平&#xff09;考试”&…

安卓系统签名的制作与使用(SignApk.jar)踩坑记录

看到这里的你应该能区分apk签名跟系统签名吧,如果无法区分的请看下面链接 android 应用的证书签名跟系统签名 看过上面的文章应该知道系统签名需要的文件清单大概有哪些 前两个是编译安卓系统时在build目录下,详细目录为 /build/target/product/security 每组签名用途不同&am…

Godot学习笔记2——GDScript变量与函数

目录 一、代码编写界面 二、变量 三、函数 四、变量的类型 Godot使用的编程语言是GDS&#xff0c;语法上与python有些类似。 一、代码编写界面 在新建的Godot项目中&#xff0c;点击“创建根节点”中的“其他节点”&#xff0c;选择“Node”。 点击场景界面右上角的绿色…