欢迎访问 生活随笔!

生活随笔

当前位置: 首页 > 编程资源 > 编程问答 >内容正文

编程问答

谷歌protobuf(Protocol buffers)的使用

发布时间:2024/3/12 编程问答 97 豆豆
生活随笔 收集整理的这篇文章主要介绍了 谷歌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++数据类型描述
    floatfloat
    doubledouble
    int32__int32使用变长编码,对于负值的效率很低,如果你的域有可能有负值,请使用sint64替代
    uint32unsigned __int32使用变长编码
    int64__int64使用变长编码
    uint64unsigned __int64使用变长编码
    sint32__int32使用变长编码,这些编码在负值时比int32高效的多
    sint64__int64使用变长编码,有符号的整型值。编码时比通常的int64高效。
    fixed32unsigned __int32总是4个字节,如果数值总是比228大的话,这个类型会比uint32高效。
    fixed64unsigned __int64总是8个字节,如果数值总是比总是比256大的话,这个类型会比uint64高效。
    sfixed32__int32总是4个字节
    sfixed64__int64总是8个字节
    boolbool
    stringstd::string一个字符串必须是UTF-8编码或者7-bit ASCII编码的文本。
    bytesstd::string可能包含任意顺序的字节数据。
    enumenum枚举类型。如果要将两个枚举变量的值设为相等,那么需要添加如下代码,否则会报错: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)的使用的全部内容,希望文章能够帮你解决所遇到的问题。

    如果觉得生活随笔网站内容还不错,欢迎将生活随笔推荐给好友。