简单记录以下protobuf2的用法,以后忘记了可以回顾一下!(这是谷歌的一个库)
mirrors / google / protobuf · GitCodeProtocol Buffers - Google's data interchange format Github 镜像仓库 源项目地址 ⬇...https://gitcode.net/mirrors/google/protobuf?utm_source=csdn_github_accelerator点击上面的连接去下载!
然后执行命令安装必要条件:
apt-get install autoconf automake libtool curl make g++ unzip
centos系统将apt-get 改为 yum
依次执行以下命令安装:(进入root用户)
unzip protobuf-master.zip
cd protobuf-master/
./autogen.sh
./configure
make
make check
make install
ldconfig
头文件所在路径:/usr/local/include/
库所在路径:/usr/local/lib
安装完毕!
编写按照如下格式去编写:
message 名字
{
required 类型 变量名 = 1;
required 类型 变量名 = 2;
optional 类型 变量名 = 3;
}
message是固定的,开头需要写上;
required是属性,一共有三种属性,分别是,required、optional、repeated
也就是说,设置了required,就必须给他设置值;
repeated int32 code= 4 [packed=true]; // 定义时可以这样,效率会高点
设置了optional,可以不用给他设置值,但要设置默认值,如下:
optional std::string data = 3 [default = 10];
设置了repeated,(可以说是枚举)也要给他设置值,且它可以被设置多个值;
类型可以是以下:(在其他博客截图的)
后面赋值 1, 2, 3,根据顺序赋值,从1开始,自增赋值即可!
如下编写案例:
ptb.proto
syntax = "proto2";
package tutorial;
message response
{
required int32 code = 1;
required int32 icode = 2;
optional string data = 3;
}
message list_account_records_response
{
required int32 code = 1;
optional string desc = 2;
message account_record
{
required int32 type = 1;
required int32 limit = 2;
required uint64 timestamp = 3;
}
repeated account_record records = 3;
}
开头一定要写上:
syntax = "proto2"; // 用的是protobuf2,所以这里写proto2
package tutorial; // 这个是命名空间
编译语法:
protoc -I=$SRC_DIR --cpp_out=$DST_DIR ptb.proto
SRC_DIR 表示proto文件所在的目录,cpp_out指定了生成的代码的路径, ptb.proto指proto文件名。
protoc -I=./ --cpp_out=./ ptb.proto
这样在当前目录生成了ptb.pb.cc和ptb.pb.h两个文件。
接下来使用代码去操作。
set_变量名(); // 设置属性值
变量名(); // 获取值
SerializeToString(); // 将类格式化为stirng类型字符处
ParseFromString(); // 将字符串转换为类
add_变量名(); // 生成一个repeated 对象返回
编译命令:g++ -std=c++11 test.cc ptb.pb.cc -lprotobuf
test.cc
#include "ptb.pb.h"
#include
#include
using namespace std;
using namespace tutorial;
int main(void) {
/* 1、 */
{
std::string data; // 存储序列化的消息
// 模拟客户端发送请求
{
response res;
res.set_code(200);
res.set_icode(123);
res.set_data("字符串");
// 将类格式化为stirng类型字符处
res.SerializeToString(&data);
// 客户端将data发送给服务器
}
// 模拟服务器接受请求
{
response res;
// 将字符串转换为
res.ParseFromString(data);
std::cout << "code = " << res.code() << std::endl;
std::cout << "icode = " << res.icode() << std::endl;
std::cout << "data = " << res.data() << std::endl;
}
}
printf("-------------华丽的分隔符-------------\n");
/* 2、 */
{
std::string data; // 存储序列化的消息
// 模拟客户端发送请求
{
list_account_records_response larr;
larr.set_code(200);
larr.set_desc("ok");
for (int i = 0; i < 2; i++) {
// 分配一个对象
list_account_records_response_account_record *ar = larr.add_records();
ar->set_type(i);
ar->set_limit(i * 100);
ar->set_timestamp(time(NULL));
}
// 输出records的个数
printf("client:recoreds size : %d\n", larr.records_size());
// 将类格式化为stirng类型字符处
larr.SerializeToString(&data);
// 客户端将data发送给服务器
}
// 模拟服务器接受请求
{
list_account_records_response larr;
larr.ParseFromString(data);
// 输出records的个数
printf("server:recoreds size : %d\n", larr.records_size());
printf("code: %d\n", larr.code());
printf("desc: %s\n", larr.desc().c_str());
for (int i = 0; i < 2; i++) {
const list_account_records_response_account_record &ar = larr.records(i);
printf("type: %d\n", ar.type());
printf("limit: %d\n", ar.limit());
printf("timestamp: %lu\n", ar.timestamp());
}
}
}
return 0;
}
client.cc
g++ -std=c++11 client.cc ptb.pb.cc -lprotobuf -levent -o client.exe
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "ptb.pb.h"
#include
#include
using namespace std;
using namespace tutorial;
#define COUNT 3
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) {
printf("please input 2 parameters!\n");
return -1;
}
// 两个参数依次是服务器的IP地址和端口号
int sockfd = connect_server(argv[1], atoi(argv[2]));
if (-1 == sockfd) {
perror("tcp_connect error!");
return -1;
}
printf("connect to server successfully\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);
// 事件循环
event_base_dispatch(base);
event_base_free(base);
printf("finished\n");
return 0;
}
void cmd_read_data(int fd, short events, void *arg) {
char msg[1024] = { '\0' };
std::string data = "";
int ret = read(fd, msg, sizeof(msg) - 1);
if (0 == ret) {
printf("connection close. exit!\n");
exit(1);
}
if (ret < 0) {
perror("read failed!");
exit (1);
}
int sockfd = *((int *)arg);
if (msg[ret - 1] == '\n') {
msg[ret - 1] = '\0';
} else {
msg[ret] = '\0';
}
{
list_account_records_response larr;
larr.set_code(200);
larr.set_desc(msg);
for (int i = 0; i < COUNT; i++) {
// 分配一个对象
list_account_records_response_account_record *ar = larr.add_records();
ar->set_type(i + 1);
ar->set_limit((i+1)*10);
ar->set_timestamp(time(NULL));
}
//printf("recoreds size: %d\n", larr.records_size());
// 将类格式化为stiring类型字符串
larr.SerializeToString(&data);
}
// 把终端的消息发送给服务器端,客户端忽略性能考虑,直接利用阻塞方式发送
//printf("write to server >>> %s\n", msg);
ret = write(sockfd, data.c_str(), data.length());
if (ret == -1) {
perror("write to server failed!");
exit(1);
}
//printf("ret = %d\n", ret);
if (strncmp(msg, "exit", 4) == 0) {
memset(msg, 0, sizeof(msg));
write(sockfd, msg, sizeof(msg));
usleep(100000); // 100ms
close(sockfd);
exit(1);
}
}
void socket_read_data(int fd, short events, void *arg) {
char msg[1024] = { '\0' };
// 不考虑一次读不完数据的情况
int len = read(fd, msg, sizeof(msg) - 1);
if (0 == len) {
printf("connection close. exit!\n");
exit(1);
} else if (len < 0) {
perror("read failed!");
return ;
}
msg[len] = '\0';
std::string data = msg;
list_account_records_response larr;
// 字符串转化为对象
larr.ParseFromString(data);
printf("code: %d\n", larr.code());
printf("desc: %s\n", larr.desc().c_str());
for (int i = 0; i < COUNT; i++) {
const list_account_records_response_account_record &ar = larr.records(i);
printf("type: %d\n", ar.type());
printf("limit: %d\n", ar.limit());
printf("time: %lu\n", ar.timestamp());
}
//printf("recv from server <<< %s\n", msg);
}
typedef struct sockaddr SA;
int 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 (0 == status) {
errno = EINVAL;
return -1;
}
sockfd = socket(PF_INET, SOCK_STREAM, 0);
status = connect(sockfd, (SA *)&server_addr, sizeof(server_addr));
if (-1 == status) {
save_errno = errno;
close(sockfd);
errno = save_errno; // the close may be error
return -1;
}
return sockfd;
}
server.cc
g++ -std=c++11 server.cc ptb.pb.cc -lprotobuf -levent -o server.exe
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "ptb.pb.h"
using namespace std;
using namespace tutorial;
#define COUNT 3
#define BUFLEN 1024
typedef struct _ConnectStat {
struct bufferevent *bev;
char buf[BUFLEN];
}ConnectStat;
ConnectStat *stat_init(struct bufferevent *bev);
void do_echo_request(struct bufferevent *bev, void *arg); // 读数据
void do_echo_response(struct bufferevent *bev, void *arg); // 写数据
void event_cb(struct bufferevent *bev, short event, void *arg); // 出错处理函数
int tcp_server_init(int port, int listen_num);
void listener_cb(struct evconnlistener *listener, evutil_socket_t fd, struct sockaddr *sock, int socklen, void *arg); // 监听函数
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);
//server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
base = event_base_new();
// 创建socket,绑定、监听、接受链接
// 创建监听对象,在指定的地址上监听接下来的TCP连接
// listen、connect、bind、accept; LEV_OPT_REUSEABLE:可重用,LEV_OPT_CLOSE_ON_FREE:自动关闭
struct evconnlistener *listener = evconnlistener_new_bind(base, listener_cb, base,
LEV_OPT_REUSEABLE | LEV_OPT_CLOSE_ON_FREE,
1024, (struct sockaddr *)&sin,
sizeof(struct sockaddr_in));
// 监听集合中的事件
event_base_dispatch(base);
// 释放
evconnlistener_free(listener);
event_base_free(base);
return 0;
}
ConnectStat *stat_init(struct bufferevent *bev) {
ConnectStat *temp = NULL;
temp = (ConnectStat *)malloc(sizeof(ConnectStat));
if (!temp) {
fprintf(stderr, "malloc failed. reason: %s\n", strerror(errno));
return NULL;
}
memset(temp, '\0', sizeof(ConnectStat));
temp->bev = bev;
return temp;
}
void do_echo_request(struct bufferevent *bev, void *arg) {
ConnectStat *stat = (ConnectStat *)arg;
char *msg = stat->buf;
std::string data = "";
// 从缓冲区中获取数据
size_t len = bufferevent_read(bev, msg, BUFLEN);
if (0 == len) {
return;
}
msg[len] = '\0';
data = msg;
{
list_account_records_response larr;
// 将字符串转换
larr.ParseFromString(data);
//printf("recoreds size: %d\n", larr.records_size());
// 输出接收到的数据
printf("code: %d\n", larr.code());
printf("desc: %s\n", larr.desc().c_str());
for (int i = 0; i < COUNT; i++) {
const list_account_records_response_account_record &ar = larr.records(i);
printf("type: %d\n", ar.type());
printf("limit: %d\n", ar.limit());
printf("time: %lu\n", ar.timestamp());
}
larr.set_desc(larr.desc() + "123");
}
//printf("recv from client <<< %s\n", msg);
// 将数据添加到缓冲区
bufferevent_write(bev, msg, strlen(msg));
}
void do_echo_response(struct bufferevent *bev, void *arg) {
return ;
}
void event_cb(struct bufferevent *bev, short event, void *arg) {
ConnectStat *stat = (ConnectStat *)arg;
if (event & BEV_EVENT_EOF) {
printf("connect cloase\n");
} else if (event & BEV_EVENT_ERROR) {
printf("some other error\n");
}
// 自动close套接字和free读写缓冲区
bufferevent_free(bev);// 释放bufferevent对象
free(stat);
}
typedef struct sockaddr SA;
int tcp_server_init(int port, int listen_num) {
int errno_save;
evutil_socket_t listener; // int listener
listener = socket(AF_INET, SOCK_STREAM, 0);
if (-1 == listener) {
return -1;
}
// 允许多次绑定同一个地址,要用在socket和bind之间
evutil_make_listen_socket_reuseable(listener);
struct sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = 0;
sin.sin_port = htons(port);
if (bind(listener, (SA *)&sin, sizeof(sin)) < 0) {
errno_save = errno;
evutil_closesocket(listener);
errno = errno_save;
return -1;
}
if (listen(listener, listen_num) < 0) {
errno_save = errno;
evutil_closesocket(listener);
errno = errno_save;
return -1;
}
// 跨平台统一接口,将套接字设置为非阻塞状态
evutil_make_socket_nonblocking(listener);
return listener;
}
// 一个客户端连接上服务器此函数就会被调用;当此函数被调用时,libevent已经帮我们accept了这个客户端
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;
// 针对已经存在的socket创建bufferevent对象
// BEV_OPT_CLOSE_ON_FREE:如果释放bufferevent对象,则关闭连接
struct bufferevent *bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
// BEV_OPT_CLOSE_ON_FREE:释放bufferevent时关闭底层传输端口。这将关闭底层套接字,释放底层bufferevent等。
ConnectStat *stat = stat_init(bev);
// 给bufferevent设置回调函数
// bufferevent对象、读事件回调函数、写事件回调函数、其他事件回调函数、参数
bufferevent_setcb(bev, do_echo_request, do_echo_response, event_cb, stat); // evnet_set
bufferevent_enable(bev, EV_READ | EV_PERSIST); // evnet_add,使bufferevent 生效
}
简单用法介绍完毕,现阶段学习中,我是这样去使用的;日后如果接触的项目有用到再来更新。