(自用)交互协议设计——protobuf序列化

protobuf是一种比json和xml等序列化工具更加轻量和高效的结构化数据存储格式,性能比json和xml真的强很多,毕竟google出品。

protobuf原理

 

protobuf如何使用

创建xxx.proto文件

开头写上

syntax="proto2"package tutorial;

表明使用的proto版本和导入tutorial包

以充值查询响应为例,对应proto文件中代码如下 

message list_account_records_response
{required int32   code   = 1;    // 响应代号optional string  desc   = 2;    // 验证码message account_record{required int32  type      = 1; // 0 : 骑行消费,  1 : 充值, 2 : 退款required int32  limit     = 2; // 消费或者充值金额required uint64 timestamp = 3; // 记录发生时的时间戳}repeated account_record records = 3;
}

PS:

可在message中自定义message类型,如上图中的account_record

编译message文件

编译语法:

protoc -I=$SRC_DIR --cpp_out=$DST_DIR  bike.proto

SRC_DIR 表示proto文件所在的目录,cpp_out指定了生成的代码的路径, bike.proto指proto文件名。

protoc -I=./ --cpp_out=./ bike.proto

这样在当前目录生成了bike.pb.cc和bike.pb.h两个文件。

编译生成的c++文件

g++  -std=c++11   example.cc bike.pb.cc -lprotobuf

std=c++11表示使用c++11的标准

protobuf使用示例代码

example.cc

#include"bike.pb.h"
#include<string>
#include<iostream>using namespace std;
using namespace tutorial;int main(void)
{std::string data;//存储序列化的消息//客户端发送请求{mobile_request mr;mr.set_mobile("18106050285");mr.SerializeToString(&data);//格式化,变成序列化的消息//客户端发送data  send(sockfd,data.c_str(),data.length())}//服务器端接收请求{//receive(sockfd,data,....);mobile_request mr;mr.ParseFromString(data);cout << "客户端手机号码:" << mr.mobile() << endl;}return 0;
}

进行编译

​
g++  -std=c++11   example.cc bike.pb.cc -lprotobuf​

生成a.out

执行a.out:

复杂一些的使用示例:

list_account_records_response对象中的records对象为可重复的,如何在一个list_account_records_response中添加多个records呢?

example1.cc

#include"bike.pb.h"
#include<string>
#include<iostream>using namespace std;
using namespace tutorial;int main(void)
{std::string data;//存储序列化的消息//客户端发送请求{list_account_records_response larr;larr.set_code(200);larr.set_desc("ok");for (size_t i = 0; i < 5; i++){list_account_records_response_account_record* ar = larr.add_records();ar->set_type(0);ar->set_limit(i*100);ar->set_timestamp(NULL);}cout << "records size :" << larr.records_size() << endl;larr.SerializeToString(&data);//客户端发送data  send(sockfd,data.c_str(),data.length())}//服务器端接收请求{//receive(sockfd,data,....);list_account_records_response larr;larr.ParseFromString(data);cout << "records size :" << larr.records_size() << endl;printf("code : %d \n",larr.code());for (int i = 0; i < larr.records_size(); i++){const list_account_records_response_account_record& ar = larr.records(i);printf("limit: %d \n",ar.limit());}}return 0;
}

Libevent与Protobuf一起协作

客户端代码

client.cc

#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<errno.h>
#include<unistd.h>#include<stdio.h>
#include<string.h>
#include<stdlib.h>#include<event.h>
#include<event2/bufferevent.h>
#include<event2/buffer.h>
#include<event2/util.h>#include"bike.pb.h"using namespace std;
using namespace tutorial;int tcp_connect_server(const char* server_ip, int port);void cmd_msg_cb(int fd,short events,void* arg);
void server_msg_cb(int fd, short events, void* arg);
void event_cb(struct bufferevent *bev,short event,void *arg);int main(int argc, char** argv)
{if( argc < 3 ){printf("please input 2 parameters\n");return -1;}//两个参数依次是服务器端的IP地址、端口号int sockfd = tcp_connect_server(argv[1], atoi(argv[2]));if( sockfd == -1){perror("tcp_connect error ");return -1;}printf("connect to server successfully\n");struct event_base* base = event_base_new();struct bufferevent* bev = bufferevent_socket_new(base, sockfd, BEV_OPT_CLOSE_ON_FREE);bufferevent_enable(bev, EV_READ | EV_PERSIST);struct event *ev_sockfd = event_new(base, sockfd,EV_READ | EV_PERSIST,server_msg_cb, NULL);event_add(ev_sockfd, NULL);//监听终端输入事件struct event* ev_cmd = event_new(base, STDIN_FILENO,EV_READ | EV_PERSIST, cmd_msg_cb,(void*)bev);event_add(ev_cmd, NULL);event_base_dispatch(base);printf("finished \n");return 0;
}void cmd_msg_cb(int fd, short events, void* arg)
{char msg[1024];string proto_msg;int ret = read(fd, msg, sizeof(msg));if( ret <= 0 ){perror("read fail ");exit(1);}cout <<"终端输入:" << msg << endl;struct bufferevent* bev = (struct bufferevent*)arg;list_account_records_request larr;larr.set_mobile("18106050285");larr.SerializeToString(&proto_msg);cout << "proto_msg 内容:"<<proto_msg.c_str() << endl;cout << "proto_msg长度为:"<<proto_msg.length() << endl;if (bev==NULL)cout << "bev为空" << endl;bufferevent_write(bev,proto_msg.c_str(),proto_msg.length());
}void server_msg_cb(int fd, short events, void* arg)
{char msg[1024];//为了简单起见,不考虑读一半数据的情况int len = read(fd, msg, sizeof(msg)-1);msg[len] = '\0';if( len == 0 ){printf("connection close. exit~\n");exit(1);}else if(len < 0){perror("read fail ");return ;}msg[len] = '\0';printf("recv from server<<<<< [%s] \n", msg);
}typedef struct sockaddr SA;
int tcp_connect_server(const char* server_ip, int port)
{int sockfd, status, save_errno;struct sockaddr_in server_addr;memset(&server_addr, 0, sizeof(server_addr) );server_addr.sin_family = AF_INET;server_addr.sin_port = htons(port);status = inet_aton(server_ip, &server_addr.sin_addr);if( status == 0 ) //the server_ip is not valid value{errno = EINVAL;return -1;}sockfd = socket(PF_INET, SOCK_STREAM, 0);if( sockfd == -1 )return sockfd;status = connect(sockfd, (SA*)&server_addr, sizeof(server_addr) );if( status == -1 ){save_errno = errno;close(sockfd);errno = save_errno; //the close may be errorreturn -1;}//evutil_make_socket_nonblocking(sockfd);return sockfd;
}
编译
g++ -std=c++11 client.cc bike.pb.cc -lprotobuf -levent -o client.exe

要加上-lprotobuf -levent 和 C++11标准

服务端代码

server.cc

#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>#include <unistd.h>
#include <event.h>
#include <event2/event.h>
#include<event2/listener.h>
#include <assert.h>
#include<string>#include"bike.pb.h"#define BUFLEN  1024using namespace std;
using namespace tutorial;typedef struct _ConnectStat {//struct event*  ev;struct bufferevent* bev;char buf[BUFLEN];
}ConnectStat;//echo 服务实现相关代码
ConnectStat * stat_init(int fd, struct bufferevent *bev);void listener_cb(struct evconnlistener *listener, evutil_socket_t fd,  struct sockaddr *sock, int socklen, void *arg); //accept_connection(int fd, short events, void* arg);
void do_echo_request(struct bufferevent* bev, void* arg);
void event_cb(struct bufferevent *bev, short event, void *arg);
//void do_echo_response(int fd, short events, void *arg);int tcp_server_init(int port, int listen_num);struct event_base* base;int main(int argc, char** argv)
{struct sockaddr_in sin;  memset(&sin, 0, sizeof(struct sockaddr_in));  sin.sin_family = AF_INET;  sin.sin_port = htons(9999);  base = event_base_new();  struct evconnlistener *listener  = evconnlistener_new_bind(base, listener_cb, base,  LEV_OPT_REUSEABLE|LEV_OPT_CLOSE_ON_FREE,  10, (struct sockaddr*)&sin,  sizeof(struct sockaddr_in));  event_base_dispatch(base);  evconnlistener_free(listener);event_base_free(base);return 0;  
}ConnectStat * stat_init(int fd, struct bufferevent *bev) {ConnectStat * temp = NULL;temp = (ConnectStat *)malloc(sizeof(ConnectStat));if (!temp) {fprintf(stderr, "malloc failed. reason: %m\n");return NULL;}memset(temp, '\0', sizeof(ConnectStat));temp->bev = bev;}/*
一个新客户端连接上服务器此函数就会被调用,当此函数被调用时,libevent已经帮我们accept了这个客户端。
该客户端的文件描述符为fd
*/
void listener_cb(struct evconnlistener *listener, evutil_socket_t fd,  struct sockaddr *sock, int socklen, void *arg)  
{  printf("accept a client %d\n", fd);  struct event_base *base = (struct event_base*)arg;  //为这个客户端分配一个bufferevent  struct bufferevent *bev =  bufferevent_socket_new(base, fd,  BEV_OPT_CLOSE_ON_FREE);ConnectStat *stat = stat_init(fd, bev);											   bufferevent_setcb(bev, do_echo_request, NULL, event_cb, stat);  bufferevent_enable(bev, EV_READ | EV_PERSIST);  
}  void do_echo_request(struct bufferevent* bev, void* arg)
{string request;ConnectStat *stat = (ConnectStat *)arg;char *msg = stat->buf;printf("do echo request ...\n");size_t len = bufferevent_read(bev, msg, BUFLEN);msg[len] = '\0';//解析protobuf 请求{list_account_records_request larr;request = msg;larr.ParseFromString(request);//反序列化printf("recv from client<<<< %s\n", larr.mobile().c_str());bufferevent_write(bev, larr.mobile().c_str(), larr.mobile().length());}}void event_cb(struct bufferevent *bev, short event, void *arg)
{ConnectStat *stat = (ConnectStat *)arg;if (event & BEV_EVENT_EOF)printf("connection closed\n");else if (event & BEV_EVENT_ERROR)printf("some other error\n");//这将自动close套接字和free读写缓冲区bufferevent_free(bev);free(stat);
}
编译

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

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

相关文章

机器学习——支持向量机(SVM)(2)

目录 一、SVC理解进阶 1. C&#xff08;硬间隔与软间隔&#xff09; 2. class_weight 二、模型评估指标&#xff08;SVC&#xff09; 1. 混淆矩阵 &#xff08;Confusion Matrix&#xff09; &#xff08;1&#xff09;准确率 —— 模型整体效果 &#xff08;2&#xff…

Spring AI 更新:支持OpenAI的结构化输出,增强对JSON响应的支持

就在昨晚&#xff0c;Spring AI发了个比较重要的更新。 由于最近OpenAI推出了结构化输出的功能&#xff0c;可确保 AI 生成的响应严格遵守预定义的 JSON 模式。此功能显着提高了人工智能生成内容在现实应用中的可靠性和可用性。Spring AI 紧随其后&#xff0c;现在也可以对Open…

STM32CubleMX创建FreeRtos工程教程,图文教程

前言&#xff1a;STM32CubeMX 是一个开发工具&#xff0c;它已经将 FreeRTOS 这个实时操作系统&#xff08;RTOS&#xff09;集成到其工具中。换句话说&#xff0c;通过 STM32CubeMX&#xff0c;可以非常方便地为 STM32 微控制器生成配置代码&#xff0c;其中包括对 FreeRTOS 的…

jdbc操作数据库MySQL

mysql创建class表 往数据库中使用代码插入一条数据 step1.创建DataSource DataSource dataSource new MysqlDataSource();((MysqlDataSource)dataSource).setUrl("jdbc:mysql://127.0.0.1:3306/java109?characterEncodingutf8&usessLfalse");((MysqlDataSour…

2024十大网站设计公司推荐TOP10

一个精心设计的网站对于企业来说不仅是品牌形象的延伸&#xff0c;也是吸引客户、提升业务的关键工具。 而选择一家专业的网站设计公司&#xff0c;不仅可以帮助企业在激烈的市场竞争中脱颖而出&#xff0c;还能收获更多的客流量&#xff0c;以下是我们精选的十大网站设计公司…

LVS负载均衡+集群+三种工作模式+调度算法及实战案例

一、LVS 1.1简介 LVS&#xff08;Linux Virtual Server&#xff09;即Linux虚拟服务器&#xff0c;是由章文嵩主导开发的开源负载均衡项目&#xff0c;目前&#xff0c;LVS已经被集成到Linux内核模块中。该项目实现了在基于IP的数据基础上&#xff0c;请求负载均衡调度方案&a…

git推送错误-->远程分支比本地的分支更新,无法直接推送

每次上传本地修改好的代码的时候,十次有八次都会出现这样的问题!!(暴躁!!!) 现在写个帖子记录一下,这个问题目前我还没有解决,欢迎懂的佬指点一下. 情景: 我在本地仓库做了一些代码的修改,准备上传到远程仓库上,下边是上传步骤: git add . # 将所有的修改都提交到缓冲区git …

工作随记:oracle中偶发遇到存储过程编辑,删除等卡死问题

文章目录 一、查询session是否占用二、通过对象名称定位对应SID三、通过对应的SID查询session信息四、kill掉session 最近有几个客户也询问过&#xff1a;我的存储过程怎么编译、调试有时候就卡死不动了&#xff0c;而且还没办法删除&#xff0c;本次又碰到实际情况&#xff0c…

sql及rce漏洞整理

sql及rce漏洞复现 一&#xff0c;mysql小特性解决大问题 <?php $mysqli new mysqli("localhost", "root", "root", "cat"); ​ /* check connection */ if ($mysqli->connect_errno) {printf("Connect failed: %s\n&qu…

前端面试题整合

一、HTML篇 1、简述一下你对HTML语义化的理解&#xff1f; 用正确的标签做正确的事情&#xff1b; HTML语义化让页面内容结构清晰&#xff0c;便于浏览器、搜索引擎解析&#xff1b; 搜索引擎的爬虫依赖HTML标记来确定上下文和关键字的权重&#xff0c;利于SEO&#xff1b; 便于…

JavaScript - 数组对象中实用好玩的reduce方法

JavaScript中reduce() 方法对数组中的每个元素执行一个由您提供的reducer函数(升序执行)&#xff0c;将其结果汇总为单个返回值。 语法&#xff1a; arr.reduce(callback(accumulator, currentValue[, index[, array]])[, initialValue]) 参数配置&#xff1a; 参数名描述cal…

渗透学习之漏洞复现

漏洞 贷齐乐的漏洞复现RCE 贷齐乐的漏洞复现 <?php header("Content-type: text/html; charsetutf-8"); require db.inc.php;function dhtmlspecialchars($string) {if (is_array($string)) {foreach ($string as $key > $val) {$string[$key] dhtmlspecial…

【Oracle点滴积累】解决ORA-20001: Latest xml inventory is not loaded into table故障的方法

广告位招租&#xff01; 知识无价&#xff0c;人有情&#xff0c;无偿分享知识&#xff0c;希望本条信息对你有用&#xff01; 今天和大家分享在安装Oracle Critical Patch Update (Patch Number:33806138) 遇到ORA-20001: Latest xml inventory is not loaded into table故障…

广东盈致MES系统——注塑和冲压行业的智能化管理

广东注塑冲压行业MES制造执行系统是一种专门为注塑和冲压行业设计的生产管理系统&#xff0c;可以帮助企业实现生产过程的智能化管理和优化。盈致MES系统是一种常见的MES系统&#xff0c;具有以下特点和功能&#xff1a; 生产计划和调度&#xff1a;MES系统可以帮助企业进行生产…

SpringCloud网关

1.网关的作用 2.网关入门 2.1引入依赖 <dependencies><dependency><groupId>com.heima</groupId><artifactId>hm-common</artifactId><version>1.0.0</version></dependency><!--网关--><dependency><g…

SAP通过函数TR_RELEASE_REQUEST释放指定请求

一&#xff1a;不通过SE09/10释放请求号 *&---------------------------------------------------------------------* *& Report Z_TRANSPORT_REQUEST *&---------------------------------------------------------------------* *& *&----------------…

AI与人类智慧的共舞:程序员在人工智能时代的新角色

文章目录 每日一句正能量前言AI辅助编程对程序员工作的影响提高编码效率改善代码质量促进学习与成长改变工作流程潜在风险与挑战技术伦理与责任应对策略结论 程序员应重点发展的核心能力复杂系统设计能力跨学科知识整合能力与AI协作的能力创新和解决问题的能力技术领导力和团队…

ctfshow-web入门-sql注入(web206-web210)系统练习sqlmap之tamper的使用与编写

目录 1、web206 2、web207 3、web208 4、web209 5、web210 1、web206 sql需要闭合 测了一下还是会先请求 /api/getToken.php 查询语句里新增了括号&#xff0c;我们注入也需要将其闭合掉&#xff0c;就像我们闭合单引号那样&#xff0c;对于 sqlmap 它会自动对闭合点进行…

HttpSession常用方法

1.HttpSession常用方法 是在Java Servlet中用来管理会话状态的重要接口&#xff0c;它提供了一种在多个请求或页面之间存储用户特定信息的方式。以下是一些 HttpSession 常用的方法和用法&#xff1a; 获取会话对象&#xff1a; HttpSession session request.getSession();…

三十八、大数据技术之Kafka3.x(1)

&#x1f33b;&#x1f33b; 目录 一、Kafka 概述1.1 定义1.2 消息队列1.2.1 消息队列内部实现原理1.2.2 传统消息队列的应用场景1.2.3 消息队列的两种模式 1.3 Kafka 基础架构 二、 Kafka 快速入门2.1 安装前的准备2.2 安装部署2.2.1 集群规划2.2.2 单节点或集群部署2.2.3 集群…