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

news2024/11/13 21:05:57

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 error
        return -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  1024

using 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.coloradmin.cn/o/2034618.html

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

相关文章

Linux:修改网卡名称(redhat-centos-redora)

解决问题: 我现在有块网卡名ens160&#xff0c;我想把他改为ens33&#xff08;仅是模拟&#xff0c;实际中你可以任意更改&#xff0c;不是局限在这两名称中&#xff0c;举一反三&#xff09; 我当前的操作系统为&#xff1a;centos9 解决办法&#xff1a; 1.修改grub配置 …

前端学习笔记-JS篇-02

运算符 赋值运算符 对变量进行赋值的运算符。 已经学过的赋值运算符:【将等号右边的值赋予给左边&#xff0c;要求左边必须是一个容器】 其他赋值运算符: - * / % 原始写法和简化写法【其实就是java基础】 一元运算符 众多的JavaScript 的运…

免费【2024】springboot 个人健康管理网站的设计与实现

博主介绍&#xff1a;✌CSDN新星计划导师、Java领域优质创作者、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和学生毕业项目实战,高校老师/讲师/同行前辈交流✌ 技术范围&#xff1a;SpringBoot、Vue、SSM、HTML、Jsp、PHP、Nodejs、Python、爬虫、数据可视化…

php 企业员工考勤系统—计算机毕业设计源码17108

摘要 由于数据库和数据仓库技术的快速发展&#xff0c;企业员工考勤系统建设越来越向模块化、智能化、自我服务和管理科学化的方向发展。员工管理系统对处理对象和服务对象&#xff0c;自身的系统结构&#xff0c;处理能力&#xff0c;都将适应技术发展的要求发生重大的变化。 …

Linux系统安全及应用(二):PAM安全认证

文章目录 4Linux中的PAM安全认证介绍su命令的安全隐患PAM认证原理和构成PAM安全认证流程PAM 配置文件结构说明PAM 控制标记的补充说明PAM 实例 4Linux中的PAM安全认证 介绍 PAM&#xff08;Pluggable Authentication Modules&#xff09;&#xff0c;可插拔式认证模块是一种高…

高翔【自动驾驶与机器人中的SLAM技术】学习笔记(七)卡尔曼滤波器三:卡尔曼滤波器公式推导【转载】

卡尔曼滤波器三&#xff1a;卡尔曼公式推导 转载来源&#xff1a;卡尔曼滤波&#xff1a;从入门到精通 简述 考虑一个SLAM 问题&#xff0c;它由一个运动方程&#xff1a; x t f ( x t − 1 , u t ) ω t (1) \mathbf{x}_{t}f(\mathbf{x}_{t-1},\mathbf{u}_{t}) \omega_…

尚品汇-ES(三十一)

目录&#xff1a; &#xff08;1&#xff09;封装搜索相关实体对象 &#xff08;2&#xff09;搜索接口封装 &#xff08;3&#xff09;在service-list-client模块添加远程接口 &#xff08;1&#xff09;封装搜索相关实体对象 搜索参数实体&#xff1a;SearchParam 搜索参…

haproxy高级功能配置

介绍HAProxy高级配置及实用案例 一.基于cookie会话保持 cookie value:为当前server指定cookie值&#xff0c;实现基于cookie的会话黏性&#xff0c;相对于基于 source 地址hash 调度算法对客户端的粒度更精准&#xff0c;但同时也加大了haproxy负载&#xff0c;目前此模式使用…

Service服务在Android中的使用

目录 一&#xff0c;Service简介 二&#xff0c;Service的两种启动方式 1&#xff0c;非绑定式启动Service 2&#xff0c;绑定式启动Service 三&#xff0c;Service的生命周期 1&#xff0c;非绑定式Service的生命周期 2&#xff0c;绑定式Service的生命周期 四&#xf…

BCArchive加密工具实测分享:为何我觉得它很实用?

前言 你是不是经常有这样的烦恼&#xff1a;重要的文件、私密的照片、敏感的资料&#xff0c;总是担心会不小心泄露出去&#xff1f;哎呀&#xff0c;别担心&#xff0c;别担心&#xff0c;我今天要介绍的这款软件&#xff0c;简直就是守护你数据安全的超级英雄&#xff01; 在…

CVE-2012-2122 mysql/mariaDB身份认证漏洞

简介&#xff1a; 当连接MariaDB/MySQL时&#xff0c;输入的密码会与期望的正确密码比较&#xff0c;不断的尝试登录连接&#xff0c;回导致MySQL认为两个密码是相同的。也就是说只要知道用户名&#xff0c;不断尝试就能够直接登入SQL数据库。 影响范围#所有的Mariadb和mysql版…

【吊打面试官系列-Elasticsearch面试题】Elasticsearch 在部署时,对 Linux 的设置有哪些优化方法?

大家好&#xff0c;我是锋哥。今天分享关于 【Elasticsearch 在部署时&#xff0c;对 Linux 的设置有哪些优化方法&#xff1f;】面试题&#xff0c;希望对大家有帮助&#xff1b; Elasticsearch 在部署时&#xff0c;对 Linux 的设置有哪些优化方法&#xff1f; 1、64 GB 内存…

【STM32】CubeMX + CLion + FreeRTOS移植过程问题记录

文章目录 一、portable 文件选择二、自定义文件添加三、ST-Link v2 烧录问题四、STM32F407工程中程序无法启动调度器 前言   本文依照稚晖君分享的配置CLion用于STM32开发【优雅の嵌入式开发】&#xff0c;尝试配置STM32CubeMX CLion开发环境&#xff0c;并在此基础上移植Fre…

利用Emgucv绘制条形码边框16(C#)

EmguCV环境配置&#xff1a; ​​​​​​Emgu CV4图像处理之环境搭建1(C#)_emgucv 4.7-CSDN博客 本文测试环境&#xff1a; win10 64位 vistual studio 2019 Emgu CV 4.6.0 环境配置准备&#xff1a; 1 新增控制台项目&#xff0c;.net framework为4.7.2 2 把win-x…

minikube 实践练习

前言 我这里就简单跟着官方教程做了下练习 参考文档&#xff1a;https://v1-27.docs.kubernetes.io/zh-cn/docs/tutorials/hello-minikube/ 这里最重要的是&#xff0c;你需要提前配置好你的网络。 这个我教不了&#xff0c;之前发了篇帖子vmware实现科学上网审核不通过&…

ElasticSearch 全文检索相关性 算分

文章目录 相关性相关性Relevance相关性算法TF-IDFBM25 通过Explain查看TF-IDFboosting query 多字段查询 相关性dis_max query最佳字段查询multi_match querybest_fields最佳匹配字段most_fields 多数字段搜索cross_fields跨字段搜索 相关性 相关性Relevance 搜索的相关性算分…

Ubuntu 通过 docker 安装 Nginx 镜像 + 创建并运行 nginx01 容器

一、安装 nginx:精简版镜像 1. 查找有什么类型的 nginx 镜像 yammiemy-pc >/home/yammie $ docker search nginx 2. 下载精简版 nginx 镜像 yammiemy-pc >/opt $ docker pull nginx:alpine alpine: Pulling from library/nginx 46b060cc2620: Already exists 21af147…

【深度学习|目标跟踪】快速入门卡尔曼滤波!

卡尔曼滤波详解 申明一、什么是卡尔曼滤波1.1 卡尔曼滤波的使用场景1.2 卡尔曼滤波的定义 二、卡尔曼滤波公式详解&#xff08;无推导&#xff09;三、卡尔曼滤波的简单应用 申明 本博客参考了b站up主“华南小虎队”的卡尔曼滤波教学视频以及Lauszus Kristian Sloth Lauszus的卡…

联想Thinkpad驱动安装下载(官网的驱动下载)

联想Thinkpad驱动安装官网下载地址&#xff1a; 联想驱动管理_ThinkPad服务网站-联想服务 联想驱动管理 帮助您更快速准确的定位驱动 自动下载安装,安装驱动不求人 软件版本&#xff1a;V2.9.0719.1104 | 大小&#xff1a;5.7M最后更新&#xff1a;2021-07-21支持系统&#…

41.【C语言之外】聊聊Cheat Engine官方教程步骤6的思考

0.看前须知 有一定指针概念的基础 推荐阅读前几篇博文&#xff1a; 19.【C语言】指针&#xff08;重难点&#xff09;&#xff08;A&#xff09; 37.【C语言】指针&#xff08;重难点&#xff09;&#xff08;B&#xff09; 38.【C语言】指针&#xff08;重难点&#xff09…