谷歌protobuf(Protocol buffers)的使用
谷歌protobuf的使用
- 一、概述
- 二、安装
- 三、protobuf中的限定符
- 四、protobuf支持的数据类型
- 五、编译
- 1. 将proto文件编译成 C++ 文件
- 2. 将编译好的文件与代码一起编译执行
- 六、应用
- 1. proto 文件的编写(文件后缀名:proto)
- 2. 基础应用
- 3. 嵌套应用(即message内嵌套message)
- 4. libevent 和 protobuf 协作
一、概述
- Protocol buffers 是 Google 的语言中立、平台中立、可扩展的结构化数据序列化机制——像 XML,但更小、更快、更简单。您可以定义一次数据的结构化方式,然后您可以使用特殊生成的源代码轻松地使用各种语言在各种数据流中写入和读取结构化数据。
- Photocol buffers 是一种比json和xml等序列化工具更加轻量和高效的结构化数据存储格式,性能比json和xml真的强很多。
- 原理图:
二、安装
# 如果在解压或安装时出现问题,执行
apt-get install autoconf automake libtool curl make g++ unzip
# 1. 从github上克隆protobuf
git clone https://github.com/protocolbuffers/protobuf
# 2. 解压下载好的文件
unzip protobuf-master.zip
# 3. 进入 protobuf-master 目录
cd protobuf-master/
# 4. 执行如下命令
./autogen.sh
./configure
make
make check
make install
ldconfig
# 5. 在 shell 下输入protoc ,如果出现 Usage: protoc [OPTION] PROTO_FILES 则成功。
三、protobuf中的限定符
限定符 | 含义 |
---|---|
required | 必填字段 |
optional | 可选字段 |
repeated | 可重复字段 |
四、protobuf支持的数据类型
protobuf数据类型 | 代表C++数据类型 | 描述 |
---|---|---|
float | float | 无 |
double | double | 无 |
int32 | __int32 | 使用变长编码,对于负值的效率很低,如果你的域有可能有负值,请使用sint64替代 |
uint32 | unsigned __int32 | 使用变长编码 |
int64 | __int64 | 使用变长编码 |
uint64 | unsigned __int64 | 使用变长编码 |
sint32 | __int32 | 使用变长编码,这些编码在负值时比int32高效的多 |
sint64 | __int64 | 使用变长编码,有符号的整型值。编码时比通常的int64高效。 |
fixed32 | unsigned __int32 | 总是4个字节,如果数值总是比228大的话,这个类型会比uint32高效。 |
fixed64 | unsigned __int64 | 总是8个字节,如果数值总是比总是比256大的话,这个类型会比uint64高效。 |
sfixed32 | __int32 | 总是4个字节 |
sfixed64 | __int64 | 总是8个字节 |
bool | bool | |
string | std::string | 一个字符串必须是UTF-8编码或者7-bit ASCII编码的文本。 |
bytes | std::string | 可能包含任意顺序的字节数据。 |
enum | enum | 枚举类型。如果要将两个枚举变量的值设为相等,那么需要添加如下代码,否则会报错:option allow_alias = true; |
五、编译
1. 将proto文件编译成 C++ 文件
# SRC_DIR: proto文件(example.proto)所在目录
# cpp_out(DST_DIR): 制定了生成代码的路径
# example.proto: 指proto文件名
protoc -I=$SRC_DIR --cpp_out=$DST_DIR example.proto# 示例:
protoc -I=./ --cpp_out=./ example.proto
# 说明:在当前目录中查找 example.proto 并将生成的文件放在当前目录下
2. 将编译好的文件与代码一起编译执行
g++ -std=c++11 example.cc example.pb.cc -lprotobuf
六、应用
1. proto 文件的编写(文件后缀名:proto)
syntax = "proto2";package example;// 使用手机号登录获取验证码(请求端)
message get_code_requset {required string mobile = 1;
}// 使用手机号登录获取验证码(响应端)
message get_code_response {required int32 code = 1; // 响应代号required int32 icode = 2; // 验证码optional string reason = 3; // 失败原因
}// 使用手机号登录(请求端)
message login_request {required string mobile = 1; // 手机号码required int32 icode = 2; // 验证码
}// 使用手机号登录(响应端)
message login_response {required int32 code = 1; // 响应代号optional string reason = 2; // 失败原因
}// 查询登录记录(请求端)
message login_record_request {required string mobile = 1; // 手机号码
}// 查询登录记录(响应端)
message login_record_response {required int32 code = 1; // 响应代号optional string reason = 2; // 失败原因message login_record {required int32 timestamp = 1; // 时间戳required string device_name = 2; // 设备名}repeated login_record records = 3;
}// 将以上代码编译成 C++ 文件
// protoc -I=./ --cpp_out=./ example.proto
2. 基础应用
#include <string>
#include <iostream>
#include "example.pb.h"int main(int argc, char *argv[]) {std::string data; // 存储序列化之后的数据// 客户端发送请求{example::get_code_requset getCodeRequest;getCodeRequest.set_mobile("19912341234");getCodeRequest.SerializeToString(&data);std::cout << "serial[" << data.length() << "]:" << data << std::endl;}// 服务器端解析数据{example::get_code_requset parse;parse.ParseFromString(data);std::cout << "mobile: " << parse.mobile() << std::endl;}return 0;
}/********************************************************************************************** 使用如下语句在shell下编译:g++ -std=c++11 example.cc example.pb.cc -o example.exe -lprotobuf
**********************************************************************************************/
3. 嵌套应用(即message内嵌套message)
#include <time.h>
#include <string>
#include <iostream>
#include "example.pb.h"int main(int argc, char *argv[]) {std::string data; // 存储序列化之后的数据// 客户端发送请求{example::login_record_response response;response.set_code(200);response.set_reason("OK");// 插入登录数据time_t now = time(NULL);for (int i = 0; i < 5; ++i) {example::login_record_response_login_record* login = response.add_records();login->set_timestamp(now + i);login->set_device_name(std::string("phone-") + std::to_string(i));}std::cout << "record size: " << response.records_size() << std::endl;// 序列化response.SerializeToString(&data);}// 服务器端解析数据{example::login_record_response response;response.ParseFromString(data);std::cout << "code: " << response.code()<< " reason:" << response.reason()<< " parse size : " << response.records_size() << std::endl;for (int i = 0; i < response.records_size(); ++i) {const example::login_record_response_login_record& login = response.records(i);std::cout << "timestamp: " << login.timestamp()<< " device_name: " << login.device_name() << std::endl;}}return 0;
}/********************************************************************************************** 使用如下语句在shell下编译:g++ -std=c++11 example.cc example.pb.cc -o example.exe -lprotobuf
**********************************************************************************************/
4. libevent 和 protobuf 协作
- 客户端
// 函数 cmd_read_data() 和 socket_read_data()
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <event.h>
#include <event2/event.h>#include <string>
#include "example.pb.h"typedef struct sockaddr sockaddr_t;
typedef struct sockaddr_in sockaddr_in_t;int connect_server(const char* server_ip, int port);void cmd_read_data(int fd, short events, void* arg);
void socket_read_data(int fd, short events, void* arg);int main(int argc, char* argv[]) {if (argc < 3) {fprintf(stderr, "please input [ipaddr][ipport]\n");return -1;}int sockfd = connect_server(argv[1], atoi(argv[2]));if (sockfd < 0) {fprintf(stderr, "connect_server(): failed!\n");return -2;}printf("connect server success!\n");struct event_base* base = event_base_new();struct event* ev_sockfd = event_new(base, sockfd, EV_READ | EV_PERSIST, socket_read_data, NULL);event_add(ev_sockfd, NULL);// 监听终端输入事件struct event* ev_cmd = event_new(base, STDIN_FILENO, EV_READ | EV_PERSIST, cmd_read_data, (void*)(&sockfd));event_add(ev_cmd, NULL);printf("event add finished!\n");event_base_dispatch(base);printf("finished!\n");return 0;
}int connect_server(const char* server_ip, int port) {int sockfd, status, save_errno;sockaddr_in_t server_addr;memset(&server_addr, 0, sizeof(sockaddr_in_t));server_addr.sin_family = AF_INET;server_addr.sin_port = htons(port);status = inet_aton(server_ip, &server_addr.sin_addr);if (!status) {errno = EINVAL;fprintf(stderr, "inet_aton() failed!\n");return -1;}sockfd = socket(PF_INET, SOCK_STREAM, 0);if (sockfd < 0) {fprintf(stderr, "socket() failed! reason: %s\n", strerror(errno));return sockfd;}status = connect(sockfd, (sockaddr_t*)(&server_addr), sizeof(server_addr));if (status < 0) {fprintf(stderr, "connect() failed! reason: %s\n", strerror(errno));save_errno = errno;close(sockfd);errno = save_errno;return -1;}// 不用设置非阻塞//evutil_make_socket_nonblocking(sockfd);return sockfd;
}void cmd_read_data(int fd, short events, void* arg) {std::string data;char msg[1024] = { 0 };read(fd, msg, sizeof(msg) - 1);// 向服务器发送手机号example::get_code_requset request;request.set_mobile("19912344321");request.SerializeToString(&data);int sockfd = *((int*)arg);// 把终端消息发送给服务器端,客户端忽略性能考虑,直接使用阻塞发送write(sockfd, data.c_str(), data.length());
}void socket_read_data(int fd, short events, void* arg) {char msg[1024];// 不考虑数据读了一半的情况int len = read(fd, msg, sizeof(msg) - 1);if (len == 0) {fprintf(stderr, "connection close!\n");exit(2);}else if (len < 0) {fprintf(stderr, "read failed!\n");exit(3);}msg[len] = '\0';example::get_code_response response;response.ParseFromString(std::string(msg));printf("code: %d, icode: %d, reason: %s\n", response.code(), response.icode(), response.reason().c_str());
}
- 服务器端
// 函数:do_recv_msg()
#include <string.h>
#include <stdlib.h>
#include <event.h>
#include <event2/event.h>
#include <event2/listener.h>
#include <event2/bufferevent.h>#include <string>
#include <iostream>
#include "example.pb.h"#define BUFFER_LENGTH 1024typedef struct event_base event_base_t;
typedef struct bufferevent bufferevent_t;
typedef struct evconnlistener evconnlistener_t;typedef struct _connect_stat {bufferevent_t* bev;char buffer[BUFFER_LENGTH];
}connect_stat_t;connect_stat_t* stat_new(bufferevent_t* bev);void listener_cb(evconnlistener_t* listener, evutil_socket_t fd, struct sockaddr* addr, int addrlen, void* user_arg);void do_recv_msg(bufferevent_t* bev, void* user_arg);
void event_cb(bufferevent_t* bev, short what, void* user_arg);int main(int argc, char* argv[]) {struct sockaddr_in sin;memset(&sin, 0, sizeof(sin));sin.sin_family = AF_INET;sin.sin_port = htons(9696); // 端口为 9696//sin.sin_addr.s_addr = htonl(INADDR_ANY);event_base_t* base = event_base_new();struct evconnlistener* listener = evconnlistener_new_bind(base, listener_cb, base,BEV_OPT_CLOSE_ON_FREE,10, (struct sockaddr*)(&sin), sizeof(sin));event_base_dispatch(base);evconnlistener_free(listener);event_base_free(base);return 0;
}connect_stat_t* stat_new(bufferevent_t* bev) {connect_stat_t* p = (connect_stat_t*)malloc(sizeof(connect_stat_t));memset(p, 0, sizeof(connect_stat_t));p->bev = bev;return p;
}void listener_cb(evconnlistener_t* listener, evutil_socket_t fd, struct sockaddr* addr, int addrlen, void* user_arg) {event_base_t* base = (event_base_t*)(user_arg);// 为客户端分配buffereventbufferevent_t* bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);connect_stat_t* stat = stat_new(bev);bufferevent_setcb(bev, do_recv_msg, NULL, event_cb, stat);bufferevent_enable(bev, EV_READ | EV_PERSIST);
}void do_recv_msg(bufferevent_t* bev, void* user_arg) {connect_stat_t* stat = (connect_stat_t*)user_arg;char* msg = stat->buffer;size_t len = bufferevent_read(bev, msg, BUFFER_LENGTH - 1);msg[len] = '\0';example::get_code_requset request;request.ParseFromString(std::string(msg));std::cout << "mobile: " << request.mobile() << std::endl;// 响应std::string data;example::get_code_response response;int icode = rand() % 100000 + 100000;response.set_code(200);response.set_icode(icode);response.set_reason("OK");response.SerializeToString(&data);bufferevent_write(bev, data.c_str(), data.length());
}void event_cb(bufferevent_t* bev, short what, void* user_arg) {connect_stat_t* stat = (connect_stat_t*)user_arg;if (what & BEV_EVENT_EOF) {printf("connection closed!\n");}else if (what & BEV_EVENT_ERROR) {printf("some other error!\n");}// 同时 关闭套接字 和 free 读写缓冲区bufferevent_free(bev);free(stat);
}
谷歌protobuf(Protocol buffers)的使用相关推荐
- ProtoBuf3语法指南(Protocol Buffers)_下
0.说明 ProtoBuf3语法指南, 又称为proto3, 是谷歌的Protocol Buffers第3个版本. 本文基于官方英文版本翻译, 加上了自己的理解少量修改, 一共分为上下两部分. 1.A ...
- ProtoBuf3语法指南(Protocol Buffers)_上
0.说明 ProtoBuf3语法指南, 又称为proto3, 是谷歌的Protocol Buffers第3个版本. 本文基于官方英文版本翻译, 加上了自己的理解少量修改, 一共分为上下两部分. 1.序 ...
- protobuf3 自定义option_ProtoBuf3语法指南(Protocol Buffers)_下
0.说明 ProtoBuf3语法指南, 又称为proto3, 是谷歌的Protocol Buffers第3个版本. 本文基于官方英文版本翻译, 加上了自己的理解少量修改, 一共分为上下两部分. 1.A ...
- 什么是Protocol Buffers / protobuf / protobuffer?一种服务器和客户端的消息交互方式
Table of Contents Protocol Buffers 定义消息类型 指定字段类型 分配字段编号 指定字段规则 添加更多消息类型 添加评论 保留字段 您产生了什么.proto? 标量值类 ...
- JSON与Protocol Buffers的一些比较
转载来源:http://www.cnblogs.com/zhubo/archive/2011/07/06/JSON_And_ProtocolBuffers.html JSON与Protocol Buf ...
- protocol buffers 序列化数据
一. protocol buffers 是什么? Protocol buffers 是一种语言中立,平台无关,可扩展的序列化数据的格式,可用于通信协议,数据存储等. Protocol buffers ...
- Golang Protocol Buffers数据格式教程
本文我们介绍如何在Golang应用中使用Protocol Buffers数据格式.包括Protocol Buffers的定义,与传统xml.json相比的优势,并通过几个示例进行实践. Protoco ...
- Protocol Buffers简明教程
随着微服务架构的流行,RPC框架渐渐地成为服务框架的一个重要部分.在很多RPC的设计中,都采用了高性能的编解码技术,Protocol Buffers就属于其中的佼佼者.Protocol Buffers ...
- Google Protocol Buffers介绍
Google Protocol Buffers(简称Protobuf),是Google的一个开源项目,它是一种结构化数据存储格式,是Google公司内部的混合语言数据标准,是一个用来序列化(将对象的状 ...
最新文章
- MSMQ: C# MSMQ编程问题
- 动态链接库编写与使用(VC6)
- noi99钉子和小球 解题报告
- Python线程、进程、进程池、协程
- CC2540 串口0 通道2配置
- 微软首款 Office 应用程序登陆 Linux
- kylin基础概念和基础性能优化
- Javascript学习之创建对象
- 修改了下exeScope的导出函数功能,让它只导出函数名。。。
- 手把手刷数据结构-1.手把手刷链表算法
- Unity3d用脚本实现Button图片的更改
- Qt for winrt结合Winrt API开发
- 全国计算机等级考试证书电子,全国计算机等级考试证书效力
- 【DR_CAN-MPC学习笔记】1.最优化控制和MPC基本概念
- 无压低温烧结银:SiC芯片封装的关键材料
- 某手机在-20度环境下,只能读到电池温度为-18℃
- “真香”的realme 真我X50 5G,正改变年轻人对5G手机的认知
- 以太坊开发测试(6) 运行《区块链技术进阶与实战》Score 电子积分系统
- python开发app的软件_如何利用python开发手机app
- JAVA 遍历数组求平均值与最大值
热门文章
- 纯CSS实现枫叶下落
- 反驳生命的起点是rna_生命起源之谜:RNA世界假说将迎来终结?
- 【论文翻译_无数据知识蒸馏_元学习_2022】Up to 100× Faster Data-free Knowledge Distillation
- python使用for循环输出0~10之间的整数_用Python编写一个程序,使用for循环输出0~10之间的整数...
- 了不起的中国公链 | Conflux AMA实录
- C语言教程-main函数
- windows下的中文文件名共享在linux下显示乱码的问题
- Ardunio开发实例-数字温度传感器
- 移动端seo优化,来自对百度移动搜索建站优化白皮书的整理
- 快速上手百度大脑人体关键点识别