基于muduo库函数实现protobuf协议的通信

文章目录

  • 先定义具体的业务请求类型
  • 2. 实现服务端提供的服务
  • protobuf_server.cpp
  • protobuf_client.cpp

建议先去了解muduo库和protobuf协议:

  1. Protobuf库的使用
  2. Muduo库介绍及使用

先定义具体的业务请求类型

先使用protobuf库创建我们所要完成的业务请求类型,英译汉和加法服务器和客⼾端

创建request.proto

syntax = "proto3";
package nzq;
//接下来定义rpc翻译请求信息结构 
message TranslateRequest {string msg = 1;
}
//接下来定义rpc翻译响应信息结构 
message TranslateResponse {string msg = 1;
}
//定义rpc加法请求信息结构 
message AddRequest {uint32 num1 = 1;uint32 num2 = 2;
}
//定义rpc加法响应信息结构 
message AddResponse {uint32 result = 1;
}

protoc --cpp_out=. request.proto

在这里插入图片描述

2. 实现服务端提供的服务

在实现具体服务前,先介绍⼀下muduo库中内部实现的关于简单的基于protobuf的接⼝类

ProtobufCodec类是muduo库中对于protobuf协议的处理类,其内部实现了onMessage回调接,对于接收到的数据进⾏基于protobuf协议的请求处理,然后将解析出的信息,存放到对应请求的protobuf请求类对象中,然后最终调⽤设置进去的消息处理回调函数进⾏对应请求的处理。

/*muduo-master/examples/protobuf/codec.h*/
typedef std::shared_ptr<google::protobuf::Message> MessagePtr;
//
// FIXME: merge with RpcCodec
//
class ProtobufCodec : muduo::noncopyable
{
public:enum ErrorCode{kNoError = 0,kInvalidLength,kCheckSumError,kInvalidNameLen,kUnknownMessageType,kParseError,};typedef std::function<void(const muduo::net::TcpConnectionPtr &,const MessagePtr &,muduo::Timestamp)>ProtobufMessageCallback;// 这⾥的messageCb是针对protobuf请求进⾏处理的函数,它声明在dispatcher.h中的ProtobufDispatcher类 explicit ProtobufCodec(const ProtobufMessageCallback &messageCb): messageCallback_(messageCb), // 这就是设置的请求处理回调函数errorCallback_(defaultErrorCallback){}// 它的功能就是接收消息,进⾏解析,得到了proto中定义的请求后调⽤设置的messageCallback_进⾏处理void onMessage(const muduo::net::TcpConnectionPtr &conn,muduo::net::Buffer *buf,muduo::Timestamp receiveTime);// 通过conn对象发送响应的接⼝void send(const muduo::net::TcpConnectionPtr &conn,const google::protobuf::Message &message){// FIXME: serialize to TcpConnection::outputBuffer()muduo::net::Buffer buf;fillEmptyBuffer(&buf, message);conn->send(&buf);}static const muduo::string &errorCodeToString(ErrorCode errorCode);static void fillEmptyBuffer(muduo::net::Buffer *buf, const google::protobuf::Message &message);static google::protobuf::Message *createMessage(const std::string &type_name);static MessagePtr parse(const char *buf, int len, ErrorCode *errorCode);private:static void defaultErrorCallback(const muduo::net::TcpConnectionPtr &,muduo::net::Buffer *,muduo::Timestamp,ErrorCode);ProtobufMessageCallback messageCallback_;ErrorCallback errorCallback_;const static int kHeaderLen = sizeof(int32_t);const static int kMinMessageLen = 2 * kHeaderLen + 2;        // nameLen + typeName +checkSum const static int kMaxMessageLen = 64 * 1024 * 1024; // same as codec_stream.hkDefaultTotalBytesLimit
};
};

ProtobufDispatcher类,这个类就⽐较重要了,这是⼀个protobuf请求的分发处理类,我们⽤⼾在使⽤的时候,就是在这个类对象中注册哪个请求应该⽤哪个业务函数进⾏处理。

它内部的onProtobufMessage接⼝就是给上边ProtobufCodec::messageCallback_设置的回调函数,相当于ProtobufCodec中onMessage接⼝会设置给服务器作为消息回调函数,其内部对于接收到的数据进⾏基于protobuf协议的解析,得到请求后,通过ProtobufDispatcher::onProtobufMessage接⼝进⾏请求分发处理,也就是确定当前请求应该⽤哪⼀个注册的业务函数进⾏处理。

typedef std::shared_ptr<google::protobuf::Message> MessagePtr;
class Callback : muduo::noncopyable
{
public:virtual ~Callback() = default;virtual void onMessage(const muduo::net::TcpConnectionPtr &,const MessagePtr &message,muduo::Timestamp) const = 0;
};
// 这是⼀个对函数接⼝进⾏⼆次封装⽣成⼀个统⼀类型对象的类
template <typename T>
class CallbackT : public Callback
{static_assert(std::is_base_of<google::protobuf::Message, T>::value,"T must be derived from gpb::Message.");public:typedef std::function<void(const muduo::net::TcpConnectionPtr &,const std::shared_ptr<T> &message,muduo::Timestamp)>ProtobufMessageTCallback;CallbackT(const ProtobufMessageTCallback &callback): callback_(callback){}void onMessage(const muduo::net::TcpConnectionPtr &conn,const MessagePtr &message,muduo::Timestamp receiveTime) const override{std::shared_ptr<T> concrete = muduo::down_pointer_cast<T>(message);assert(concrete != NULL);callback_(conn, concrete, receiveTime);}private:ProtobufMessageTCallback callback_;
};
// 这是⼀个protobuf请求分发器类,需要⽤⼾注册不同请求的不同处理函数,
// 注册完毕后,服务器收到指定请求就会使⽤对应接⼝进⾏处理
class ProtobufDispatcher
{
public:typedef std::function<void(const muduo::net::TcpConnectionPtr &,const MessagePtr &message,muduo::Timestamp)>ProtobufMessageCallback;// 构造对象时需要传⼊⼀个默认的业务处理函数,以便于找不到对应请求的处理函数时调⽤。explicit ProtobufDispatcher(const ProtobufMessageCallback &defaultCb): defaultCallback_(defaultCb){}// 这个是⼈家实现的针对proto中定义的类型请求进⾏处理的函数,内部会调⽤我们⾃⼰传⼊的业务处理函数void onProtobufMessage(const muduo::net::TcpConnectionPtr &conn,const MessagePtr &message,muduo::Timestamp receiveTime) const{CallbackMap::const_iterator it = callbacks_.find(message->GetDescriptor());if (it != callbacks_.end()){it->second->onMessage(conn, message, receiveTime);}else{defaultCallback_(conn, message, receiveTime);}}/*这个接⼝⾮常巧妙,基于proto中的请求类型将我们⾃⼰的业务处理函数与对应的请求给关联起来了相当于通过这个成员变量中的CallbackMap能够知道收到什么请求后应该⽤什么处理函数进⾏处理简单理解就是注册针对哪种请求--应该⽤哪个我们⾃⼰的函数进⾏处理的映射关系但是我们⾃⼰实现的函数中,参数类型都是不⼀样的⽐如翻译有翻译的请求类型,加法有加法请求类型⽽map需要统⼀的类型,这样就不好整了,所以⽤CallbackT对我们传⼊的接⼝进⾏了⼆次封装。*/template <typename T>void registerMessageCallback(const typename CallbackT<T>::ProtobufMessageTCallback &callback){std::shared_ptr<CallbackT<T>> pd(new CallbackT<T>(callback));callbacks_[T::descriptor()] = pd;}private:typedef std::map<const google::protobuf::Descriptor *,std::shared_ptr<Callback>>CallbackMap;CallbackMap callbacks_;ProtobufMessageCallback defaultCallback_;
};

⽽能实现请求与函数之间的映射,还有⼀个⾮常重要的元素:那就是应⽤层协议

在这里插入图片描述

protobuf根据我们的proto⽂件⽣成的代码中,会⽣成对应类型的类,⽐如TranslateRequest对应了⼀个TranslateRequest类,⽽不仅仅如此,protobuf⽐我们想象中做的事情更多,每个对应的类中,都包含有⼀个描述结构的指针:

在这里插入图片描述
这个描述结构⾮常重要,其内部可以获取到当前对应类类型名称,以及各项成员的名称,因此通过这些名称,加上协议中的typename字段,就可以实现完美的对应关系了.

在这里插入图片描述

protobuf_server.cpp

服务端同之前实现的muduo库的翻译服务器区别不大,加上protobuf协议后实际上就多了两个类成员:

  1. 请求分发器对象–要向其中注册请求处理函数 ProtobufDispatcher _dispatcher;
  2. protobuf协议处理器–针对收到的请求数据进行protobuf协议处理 rotobufCodec _codec;
#include "muduo/proto/codec.h"
#include "muduo/proto/dispatcher.h"
#include "muduo/base/Logging.h"
#include "muduo/base/Mutex.h"
#include "muduo/net/EventLoop.h"
#include "muduo/net/TcpServer.h"#include "request.pb.h"
#include <iostream>
#include <unordered_map>class Server {public:typedef std::shared_ptr<google::protobuf::Message> MessagePtr;typedef std::shared_ptr<nzq::TranslateRequest> TranslateRequestPtr;typedef std::shared_ptr<nzq::AddRequest> AddRequestPtr;Server(int port): _server(&_baseloop, muduo::net::InetAddress("0.0.0.0", port), "Server", muduo::net::TcpServer::kReusePort),_dispatcher(std::bind(&Server::onUnknownMessage, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)),_codec(std::bind(&ProtobufDispatcher::onProtobufMessage, &_dispatcher, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)){//注册业务请求处理函数//messagecallback有两个是因为要完成翻译和加法两个业务_dispatcher.registerMessageCallback<nzq::TranslateRequest>(std::bind(&Server::onTranslate, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));_dispatcher.registerMessageCallback<nzq::AddRequest>(std::bind(&Server::onAdd, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));_server.setMessageCallback(std::bind(&ProtobufCodec::onMessage, &_codec,std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));_server.setConnectionCallback(std::bind(&Server::onConnection, this, std::placeholders::_1));}void start() {_server.start();_baseloop.loop();}private:std::string translate(const std::string &str) {static std::unordered_map<std::string, std::string> dict_map = {{"hello", "你好"},{"Hello", "你好"},{"你好", "Hello"},{"吃了吗", "油泼面"}};auto it = dict_map.find(str);if (it == dict_map.end()) {return "没听懂!!";}return it->second;}void onTranslate(const muduo::net::TcpConnectionPtr& conn, const TranslateRequestPtr& message, muduo::Timestamp) {//1. 提取message中的有效消息,也就是需要翻译的内容std::string req_msg = message->msg();//2. 进行翻译,得到结果std::string rsp_msg = translate(req_msg);//3. 组织protobuf的响应nzq::TranslateResponse resp;resp.set_msg(rsp_msg);//4. 发送响应_codec.send(conn, resp);}void onAdd(const muduo::net::TcpConnectionPtr& conn, const AddRequestPtr& message, muduo::Timestamp) {int num1 = message->num1();int num2 = message->num2();int result = num1 + num2;nzq::AddResponse resp;resp.set_result(result);_codec.send(conn, resp);}void onUnknownMessage(const muduo::net::TcpConnectionPtr& conn, const MessagePtr& message, muduo::Timestamp) {LOG_INFO << "onUnknownMessage: " << message->GetTypeName();conn->shutdown();}void onConnection(const muduo::net::TcpConnectionPtr &conn) {if (conn->connected()) {LOG_INFO << "新连接建立成功!";}else {LOG_INFO << "连接即将关闭!";}}private:muduo::net::EventLoop _baseloop;muduo::net::TcpServer _server;//服务器对象ProtobufDispatcher _dispatcher;//请求分发器对象--要向其中注册请求处理函数ProtobufCodec _codec;//protobuf协议处理器--针对收到的请求数据进行protobuf协议处理
};int main()
{Server server(8085);server.start();return 0;
}

protobuf_client.cpp

服务端同之前实现的muduo库的翻译服务器区别不大,加上protobuf协议后实际上就多了两个类成员:

  1. 请求分发器对象–要向其中注册请求处理函数 ProtobufDispatcher _dispatcher;
  2. protobuf协议处理器–针对收到的请求数据进行protobuf协议处理 rotobufCodec _codec;
#include "muduo/proto/dispatcher.h"
#include "muduo/proto/codec.h"
#include "muduo/base/Logging.h"
#include "muduo/base/Mutex.h"
#include "muduo/net/EventLoop.h"
#include "muduo/net/TcpClient.h"
#include "muduo/net/EventLoopThread.h"
#include "muduo/base/CountDownLatch.h"#include "request.pb.h"
#include <iostream>class Client {public:typedef std::shared_ptr<google::protobuf::Message> MessagePtr;typedef std::shared_ptr<nzq::AddResponse> AddResponsePtr;typedef std::shared_ptr<nzq::TranslateResponse> TranslateResponsePtr;Client(const std::string &sip, int sport):_latch(1), _client(_loopthread.startLoop(), muduo::net::InetAddress(sip, sport), "Client"),_dispatcher(std::bind(&Client::onUnknownMessage, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)),_codec(std::bind(&ProtobufDispatcher::onProtobufMessage, &_dispatcher, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)){_dispatcher.registerMessageCallback<nzq::TranslateResponse>(std::bind(&Client::onTranslate, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));_dispatcher.registerMessageCallback<nzq::AddResponse>(std::bind(&Client::onAdd, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));_client.setMessageCallback(std::bind(&ProtobufCodec::onMessage, &_codec,std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));_client.setConnectionCallback(std::bind(&Client::onConnection, this, std::placeholders::_1));      }void connect() {_client.connect();_latch.wait();//阻塞等待,直到连接建立成功}void Translate(const std::string &msg){bit::TranslateRequest req;req.set_msg(msg);send(&req);}void Add(int num1, int num2) {bit::AddRequest req;req.set_num1(num1);req.set_num2(num2);send(&req);}private:bool send(const google::protobuf::Message *message) {if (_conn->connected()) {//连接状态正常,再发送,否则就返回false_codec.send(_conn, *message);return true;}return false;}  void onTranslate(const muduo::net::TcpConnectionPtr& conn, const TranslateResponsePtr& message, muduo::Timestamp) {std::cout << "翻译结果:" << message->msg() << std::endl;}void onAdd(const muduo::net::TcpConnectionPtr& conn, const AddResponsePtr& message, muduo::Timestamp) {std::cout << "加法结果:" << message->result() << std::endl;}void onUnknownMessage(const muduo::net::TcpConnectionPtr& conn, const MessagePtr& message, muduo::Timestamp) {LOG_INFO << "onUnknownMessage: " << message->GetTypeName();conn->shutdown();}void onConnection(const muduo::net::TcpConnectionPtr&conn){if (conn->connected()) {_latch.countDown();//唤醒主线程中的阻塞_conn = conn;}else {//连接关闭时的操作_conn.reset();}}private:muduo::CountDownLatch _latch;//实现同步的muduo::net::EventLoopThread _loopthread;//异步循环处理线程muduo::net::TcpConnectionPtr _conn;//客户端对应的连接muduo::net::TcpClient _client;//客户端ProtobufDispatcher _dispatcher;//请求分发器ProtobufCodec _codec;//协议处理器
};int main() 
{Client client("127.0.0.1", 8085);client.connect();client.Translate("hello");client.Add(11, 22);sleep(1);return 0;
}

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

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

相关文章

YOLOv11改进 | 主干篇 | YOLOv11引入MobileNetV4

1. MobileNetV4介绍 1.1 摘要&#xff1a; 我们推出了最新一代的 MobileNet&#xff0c;称为 MobileNetV4 (MNv4)&#xff0c;具有适用于移动设备的通用高效架构设计。 在其核心&#xff0c;我们引入了通用倒瓶颈&#xff08;UIB&#xff09;搜索块&#xff0c;这是一种统一且…

【MAUI】CommunityToolkit社区工具包介绍

一、为什么需要声明式开发 .NET的MVVM,始于WPF,很古典,它甚至可能是现代前端框架“声明式开发”的鼻祖。声明式开发,之所以出现,是因为命令式开发在UI层和代码层上无法解耦的问题。如下图所示: 1、命令式开发:后台代码需要调用UI层的控件(label.Text),如果更新UI层…

CEPH的写入流程

1、客户端程序发起对文件的读写请求&#xff0c;ceph前端接口&#xff08;RADOS Gateway&#xff09;将文件切分成多个固定大小的对象&#xff08;默认大小为4MB&#xff09; 2、计算文件到对象的映射 (1) 计算OID为每个对象分配一个唯一的OID&#xff08;Object ID&#xff09…

【微服务】初识(day1)

基础概念 集群 集群是将一个系统完整的部署到多个服务器&#xff0c;每个服务器提供系统的所有服务&#xff0c;多个服务器可以通过负载均衡完成任务&#xff0c;每个服务器都可以称为集群的节点。 分布式 分布式是将一个系统拆分为多个子系统&#xff0c;多个子系统部署在…

免费录屏软件工具:助力高效屏幕录制

录屏已经成为了一项非常实用且广泛应用的技术。无论是制作教学视频、记录游戏精彩瞬间&#xff0c;还是进行软件操作演示等&#xff0c;我们都常常需要一款可靠的录屏软件。今天&#xff0c;就让我们一起来探索那些功能强大录屏软件免费版&#xff0c;看看它们是如何满足我们多…

Leecode刷题之路第六天之Z字形变换

题目出处 06-Z字形变换 题目描述 个人解法 思路&#xff1a; todo 代码示例&#xff1a;&#xff08;Java&#xff09; todo复杂度分析 todo 官方解法 06-Z字形变换官方解法 方法1&#xff1a;利用二维矩阵模拟 思路&#xff1a; 代码示例&#xff1a;&#xff08;Java&am…

蓝桥杯【物联网】零基础到国奖之路:十五. 扩展模块之双路ADC

蓝桥杯【物联网】零基础到国奖之路:十五. 扩展模块之双路ADC 第一节 硬件解读第二节 CubeMX配置第三节 代码编写 第一节 硬件解读 STM32的ADC是12位&#xff0c;通过硬件过采样扩展到16位&#xff0c;模数转换器嵌入到STM32L071xx器件中。有16个外部通道和2个内部通道&#xf…

MongoDB微服务部署

一、安装MongoDB 1.在linux中拉去MongoDB镜像文件 docker pull mongo:4.4.18 2. 2.创建数据挂载目录 linux命令创建 命令创建目录: mkdir -p /usr/local/docker/mongodb/data 可以在sshclient工具查看是否创建成功。 进入moogodb目录&#xff0c;给data赋予权限777 cd …

后台管理系统脚手架

后台管理系统脚手架 介绍 在快速迭代的软件开发世界里&#xff0c;时间就是生产力&#xff0c;效率决定成败。对于构建复杂而庞大的后台系统而言&#xff0c;一个高效、可定制的后台脚手架&#xff08;Backend Scaffold&#xff09;无疑是开发者的得力助手。 脚手架 后台脚…

自动驾驶系列—自动驾驶发展史介绍

&#x1f31f;&#x1f31f; 欢迎来到我的技术小筑&#xff0c;一个专为技术探索者打造的交流空间。在这里&#xff0c;我们不仅分享代码的智慧&#xff0c;还探讨技术的深度与广度。无论您是资深开发者还是技术新手&#xff0c;这里都有一片属于您的天空。让我们在知识的海洋中…

C语言、Eazy_x——井字棋

#include<graphics.h>char board_data[3][3] { { -,-,-},{ -,-,-},{ -,-,-}, };char current_piece o;//检测指定棋子玩家是否获胜 bool CheckWin(char c) {if (board_data[0][0] c && board_data[0][1] c && board_data[0][2] c)return true;if (…

WPS使用越来越卡顿

UOS统信wps频繁的使用后出现卡顿问题&#xff0c;通过删除或重命名kingsoft文件缓存目录。 文章目录 一、问题描述二、问题原因三、解决方案步骤一步骤二步骤三 一、问题描述 用户在频繁的使用wps处理工作&#xff0c;在使用一段时间后&#xff0c;用户反馈wps打开速度慢&…

c++primier第十二章类和动态内存

本章内容包括&#xff1a; 对类成员使用动态内存分配隐式和显式地复制构造函数隐式和显式地重载赋值操作符在构造函数中使用new所必须完成的工作使用静态类成员 将布局new操作符用于对象使用指向对象的指针实现队列抽象数据类型(ADT) 动态内存和类 复习范例和静态类成员 首…

《动手学深度学习》笔记2.2——神经网络从基础→进阶 (参数管理-每层的权重/偏置)

目录 0. 前言 正文&#xff1a;参数管理 1. 参数访问 1.1 [目标参数] 1.2 [一次性访问所有参数] 1.3 [从嵌套块收集参数] 2. 参数初始化 2.1 [内置初始化] 2.2 [自定义初始化] 2.3 [参数绑定-共享参数] 3. 小结&#xff08;第2节&#xff09; 4. 延后初始化 (原书第…

AR 眼镜之-蓝牙电话-来电铃声与系统音效

目录 &#x1f4c2; 前言 AR 眼镜系统版本 蓝牙电话 来电铃声 系统音效 1. &#x1f531; Android9 原生的来电铃声&#xff0c;走的哪个通道&#xff1f; 2. &#x1f4a0; Android9 原生的来电铃声&#xff0c;使用什么播放&#xff1f; 2.1 来电铃声创建准备 2.2 来…

国庆普及模拟2总结

目录 题目链接&#xff1a; 官方题解&#xff1a; 概述&#xff1a; 总结反思&#xff1a; 题目 T1: 题目分析&#xff1a; 错误代码&#xff1a; 错因&#xff1a; &#xff21;&#xff23;代码&#xff1a; T2&#xff1a; 题目分析&#xff1a; 赛时代码&#xf…

LeetCode[中等] 55.跳跃游戏

给你一个非负整数数组 nums &#xff0c;你最初位于数组的 第一个下标 。数组中的每个元素代表你在该位置可以跳跃的最大长度。 判断你是否能够到达最后一个下标&#xff0c;如果可以&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 思路 贪心算法 可达位置…

CSS中字体图标的使用

引言&#xff1a; 在网页设计当中&#xff0c;会有很多很简洁的图标&#xff0c;比如箭头&#xff0c;照相机&#xff0c;放大镜等 这些大概率都是使用字体图标来完成的&#xff0c;因为字体图标比较简洁高效&#xff0c;不会像图片一样需要向浏览器请求数据。那么字体图标该…

记一次vue路由跳转登陆之前的页面,参数丢失问题

一、背景 vue3.0,项目登陆之前访问某个可访问的页面,当跳转到需要登陆才能访问的页面时,跳转到登陆页面,登陆后再跳转到登陆之前需要登陆才能访问的页面,跳转时发现参数丢失了。 A页面(无需登陆)===> B页面(需要登陆)====> 如果未登陆跳转到C登陆页面 ===>…

什么是文件完整性监控(FIM)

组织经常使用基于文件的系统来组织、存储和管理信息。文件完整性监控&#xff08;FIM&#xff09;是一种用于监控和验证文件和系统完整性的技术&#xff0c;识别用户并提醒用户对文件、文件夹和配置进行未经授权或意外的变更是 FIM 的主要目标&#xff0c;有助于保护关键数据和…