一、概述
- Protocol buffers 是 Google 的语言中立、平台中立、可扩展的结构化数据序列化机制——像 XML,但更小、更快、更简单。您可以定义一次数据的结构化方式,然后您可以使用特殊生成的源代码轻松地使用各种语言在各种数据流中写入和读取结构化数据。
- Photocol buffers 是一种比json和xml等序列化工具更加轻量和高效的结构化数据存储格式,性能比json和xml真的强很多。
- 原理图:

二、安装
apt-get install autoconf automake libtool curl make g++ unzip
git clone https://github.com/protocolbuffers/protobuf
unzip protobuf-master.zip
cd protobuf-master/
./autogen.sh
./configure
make
make check
make install
ldconfig
三、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++ 文件
protoc -I=$SRC_DIR --cpp_out=$DST_DIR example.proto
protoc -I=./ --cpp_out=./ 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;
}
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;
}
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;
}
4. libevent 和 protobuf 协作
- 客户端
#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;
}
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());
}
- 服务器端
#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 1024
typedef 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);
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);
bufferevent_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");
}
bufferevent_free(bev);
free(stat);
}