日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

谷歌protobuf(Protocol buffers)的使用

發(fā)布時(shí)間:2024/3/12 编程问答 66 豆豆
生活随笔 收集整理的這篇文章主要介紹了 谷歌protobuf(Protocol buffers)的使用 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

谷歌protobuf的使用

  • 一、概述
  • 二、安裝
  • 三、protobuf中的限定符
  • 四、protobuf支持的數(shù)據(jù)類型
  • 五、編譯
    • 1. 將proto文件編譯成 C++ 文件
    • 2. 將編譯好的文件與代碼一起編譯執(zhí)行
  • 六、應(yīng)用
    • 1. proto 文件的編寫(文件后綴名:proto)
    • 2. 基礎(chǔ)應(yīng)用
    • 3. 嵌套應(yīng)用(即message內(nèi)嵌套message)
    • 4. libevent 和 protobuf 協(xié)作

一、概述

  • Protocol buffers 是 Google 的語言中立、平臺(tái)中立、可擴(kuò)展的結(jié)構(gòu)化數(shù)據(jù)序列化機(jī)制——像 XML,但更小、更快、更簡(jiǎn)單。您可以定義一次數(shù)據(jù)的結(jié)構(gòu)化方式,然后您可以使用特殊生成的源代碼輕松地使用各種語言在各種數(shù)據(jù)流中寫入和讀取結(jié)構(gòu)化數(shù)據(jù)。
  • Photocol buffers 是一種比json和xml等序列化工具更加輕量和高效的結(jié)構(gòu)化數(shù)據(jù)存儲(chǔ)格式,性能比json和xml真的強(qiáng)很多。
  • 原理圖:
  • 二、安裝

    # 如果在解壓或安裝時(shí)出現(xiàn)問題,執(zhí)行 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. 進(jìn)入 protobuf-master 目錄 cd protobuf-master/ # 4. 執(zhí)行如下命令 ./autogen.sh ./configure make make check make install ldconfig # 5. 在 shell 下輸入protoc ,如果出現(xiàn) Usage: protoc [OPTION] PROTO_FILES 則成功。

    三、protobuf中的限定符

    限定符含義
    required必填字段
    optional可選字段
    repeated可重復(fù)字段

    四、protobuf支持的數(shù)據(jù)類型

    protobuf數(shù)據(jù)類型代表C++數(shù)據(jù)類型描述
    floatfloat
    doubledouble
    int32__int32使用變長(zhǎng)編碼,對(duì)于負(fù)值的效率很低,如果你的域有可能有負(fù)值,請(qǐng)使用sint64替代
    uint32unsigned __int32使用變長(zhǎng)編碼
    int64__int64使用變長(zhǎng)編碼
    uint64unsigned __int64使用變長(zhǎng)編碼
    sint32__int32使用變長(zhǎng)編碼,這些編碼在負(fù)值時(shí)比int32高效的多
    sint64__int64使用變長(zhǎng)編碼,有符號(hào)的整型值。編碼時(shí)比通常的int64高效。
    fixed32unsigned __int32總是4個(gè)字節(jié),如果數(shù)值總是比228大的話,這個(gè)類型會(huì)比uint32高效。
    fixed64unsigned __int64總是8個(gè)字節(jié),如果數(shù)值總是比總是比256大的話,這個(gè)類型會(huì)比uint64高效。
    sfixed32__int32總是4個(gè)字節(jié)
    sfixed64__int64總是8個(gè)字節(jié)
    boolbool
    stringstd::string一個(gè)字符串必須是UTF-8編碼或者7-bit ASCII編碼的文本。
    bytesstd::string可能包含任意順序的字節(jié)數(shù)據(jù)。
    enumenum枚舉類型。如果要將兩個(gè)枚舉變量的值設(shè)為相等,那么需要添加如下代碼,否則會(huì)報(bào)錯(cuò):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 # 說明:在當(dāng)前目錄中查找 example.proto 并將生成的文件放在當(dāng)前目錄下

    2. 將編譯好的文件與代碼一起編譯執(zhí)行

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

    六、應(yīng)用

    1. proto 文件的編寫(文件后綴名:proto)

    syntax = "proto2";package example;// 使用手機(jī)號(hào)登錄獲取驗(yàn)證碼(請(qǐng)求端) message get_code_requset {required string mobile = 1; }// 使用手機(jī)號(hào)登錄獲取驗(yàn)證碼(響應(yīng)端) message get_code_response {required int32 code = 1; // 響應(yīng)代號(hào)required int32 icode = 2; // 驗(yàn)證碼optional string reason = 3; // 失敗原因 }// 使用手機(jī)號(hào)登錄(請(qǐng)求端) message login_request {required string mobile = 1; // 手機(jī)號(hào)碼required int32 icode = 2; // 驗(yàn)證碼 }// 使用手機(jī)號(hào)登錄(響應(yīng)端) message login_response {required int32 code = 1; // 響應(yīng)代號(hào)optional string reason = 2; // 失敗原因 }// 查詢登錄記錄(請(qǐng)求端) message login_record_request {required string mobile = 1; // 手機(jī)號(hào)碼 }// 查詢登錄記錄(響應(yīng)端) message login_record_response {required int32 code = 1; // 響應(yīng)代號(hào)optional string reason = 2; // 失敗原因message login_record {required int32 timestamp = 1; // 時(shí)間戳required string device_name = 2; // 設(shè)備名}repeated login_record records = 3; }// 將以上代碼編譯成 C++ 文件 // protoc -I=./ --cpp_out=./ example.proto

    2. 基礎(chǔ)應(yīng)用

    #include <string> #include <iostream> #include "example.pb.h"int main(int argc, char *argv[]) {std::string data; // 存儲(chǔ)序列化之后的數(shù)據(jù)// 客戶端發(fā)送請(qǐng)求{example::get_code_requset getCodeRequest;getCodeRequest.set_mobile("19912341234");getCodeRequest.SerializeToString(&data);std::cout << "serial[" << data.length() << "]:" << data << std::endl;}// 服務(wù)器端解析數(shù)據(jù){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. 嵌套應(yīng)用(即message內(nèi)嵌套message)

    #include <time.h> #include <string> #include <iostream> #include "example.pb.h"int main(int argc, char *argv[]) {std::string data; // 存儲(chǔ)序列化之后的數(shù)據(jù)// 客戶端發(fā)送請(qǐng)求{example::login_record_response response;response.set_code(200);response.set_reason("OK");// 插入登錄數(shù)據(jù)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);}// 服務(wù)器端解析數(shù)據(jù){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 協(xié)作

  • 客戶端
  • // 函數(shù) 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);// 監(jiān)聽終端輸入事件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;}// 不用設(shè)置非阻塞//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);// 向服務(wù)器發(fā)送手機(jī)號(hào)example::get_code_requset request;request.set_mobile("19912344321");request.SerializeToString(&data);int sockfd = *((int*)arg);// 把終端消息發(fā)送給服務(wù)器端,客戶端忽略性能考慮,直接使用阻塞發(fā)送write(sockfd, data.c_str(), data.length()); }void socket_read_data(int fd, short events, void* arg) {char msg[1024];// 不考慮數(shù)據(jù)讀了一半的情況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()); }
  • 服務(wù)器端
  • // 函數(shù):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;// 響應(yīng)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");}// 同時(shí) 關(guān)閉套接字 和 free 讀寫緩沖區(qū)bufferevent_free(bev);free(stat); }

    總結(jié)

    以上是生活随笔為你收集整理的谷歌protobuf(Protocol buffers)的使用的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。