Protobuf使用总结

15 篇文章 0 订阅

Protobuf学习

google protobuf安装与使用

protobuf c++入门

ProtoBuf 官方文档

深入 ProtoBuf - 简介

[翻译] ProtoBuf 官方文档(一)- 开发者指南

[翻译] ProtoBuf 官方文档(二)- 语法指引(proto2)

[翻译] ProtoBuf 官方文档(三)- 语法指引(proto3)

[翻译] ProtoBuf 官方文档(四)- 规范指引

[翻译] ProtoBuf 官方文档(五)- 编码

[翻译] ProtoBuf 官方文档(六)- 相关技术

[翻译] ProtoBuf 官方文档(七)- 第三方组件

[翻译] ProtoBuf 官方文档(八)- (开发)教程

[翻译] ProtoBuf 官方文档(九)- (C++开发)教程

 

 

protobuf的使用误区(cpp)

根据我使用中发现的问题,protobuf的使用需要慎重使用ParseFromString来实现反序列化的。
有兴趣深究protobuf反序列化原理的兄台可以瞅瞅序列化之后的内容,好像是如下格式
一个字节的转译字符 | member name| member value如果有一个memory buffer,现在需要反序列化为一个类,万不可使用ParseFromString,隐藏着潜在的bug。如果buffer中有非字符内容,肯定反序列化的结果是有问题的。截断的情况是隐藏的,视buffer内容而定,最关键的一点,发生截断的时候也没有异常。其场景如下代码(错误用法)

char * buf ;...获取内存快内容string str = buf ;proto.ParseFromString(str)
此时需要做如下的操作:
用memory buffer实例化stringstream对象,
调用stringstream的pubsetbuf(basebuf, length)函数将内存块填充进去,注意,此处不可以将buffer赋值给一个string对象,然后用string对象构造一个stringstream。这时候会发生截断。
看到github上面的很多人的使用都是错误的。本来想找一个epoll+protobuf的基础工程的代码的,看来这个工作还得自己去做。
其问题可以简化用以下代码描述。(错误用法)

char * buf ;...string str = buf ;stringstream s(str) ;proto.ParseFromIstream(s);
如下代码段的用法为正确用法


stringstream s;s.rdbuf()->pubsetbuf(buf, length);proto.ParseFromIstream(&s) ;    
注意上面的length不能用strlen(buf)来获取。
--------------------------------------------------------------------------------------------------------------------------------

Protobuf的坑——解析的内容超出64M导致失败

工作中Protobuf有两种常见用法:1,将数据序列化到buffer中,通过消息发送出去;2,将数据序列化到文件中,通过文件进行数据传递。在第一种使用场景下,一般都会注意到数据的大小,因为消息过大会导致发送和接收时处理多个分片,降低效率,几百KB就算多的了;而在第二种场景下不知不觉序列化后的文件就增长到了几十MB。

 肆意增大的文件导致解析时执行ParseFromString()失败。一开始没想到是数据太大导致的,反复调试才意识到是数据大小影响了解析结果。在网上搜索发现protobuf有一个默认大小限制,要解析的数据不能超过默认的64MB,超出后解析失败。我奢望着别的反序列化接口没有这个限制,但是事实证明parseFromArray(), ParseFromFileDescriptor()都有这种问题。

 大家给出的解决办法就是通过CodedInputStream类的SetTotalBytesLimit()方法提高对数据大小的限制,此处仅以解析文件为例编写示例代码:

#include <google/protobuf/io/coded_stream.h>
#include <google/protobuf/io/zero_copy_stream_impl.h>
DataProtoBuf::DataArray  dataArray;
int fd = open("data_paras", O_RDONLY);//文件 data_paras里记录了序列化的protobuf数据 
::google::protobuf::io::FileInputStream input(fd));
::google::protobuf::io::CodedInputStream decoder(&input);
decoder.SetTotalBytesLimit(128*1024*1024, -1);//能解析的数据最大为128M 
bool success = dataArray.ParseFromCodedStream(&decoder) && decoder.ConsumedEntireMessage();


-----------------------

Protobuf c++使用小坑(set_allocated函数)

protobuf是后台开发中,比较常用的数据通信协议。相对于json,具有数据压缩率高等优点。但是,在某些情况下,稍不留神容易用错。

最近使用protobuf的时候,使用了相对陌生的复合类型的赋值。结果用错了,然后就莫名其妙core dump了。
使用的数据类型简化如下:

message Answer 
{
    optional uint32 choice = 1;
    optional uint32 id = 2;
}

message Detail
{
    optional Answer answer = 1;
    optional uint32 id  = 2;
}

message Rsp
{
    repeated Detail detail = 1;
}


message Info
{
    repeated Answer answer = 1
}
大概的使用过程是这样的:需要在一个for循环里面,把一个Class中的每一个student拷贝到一个局部的Family对象中的owner中。查看了protoc生成的pb.h文件之后,发现有一个set_allocated_owner(::Person* person)这样的函数。所以,我就使用了如下的代码:

//info 是一个已经定义好的Info对象Rsp rsp;for(int index = 0;index<info.answer_size();index++){    Detail * detail = rsp.add_detail();    Answer temp_ans = info.answer(index);    detail.set_allocated_answer(&temp_ans);}//do something with rsp 

然后就coredump了,用gdb调试的时候,发现一堆和protobuf相关的东西,猜测应该是protobuf用错了,但是想了很久都没有想到是什么。
但是凭借直觉,这种coredump一般都是和指针相关,想到也就这个地方用了指针,就果断google了一发protobuf的set_allocated相关的函数,发现果然是用错了。

看到了这篇文章:http://blog.csdn.net/xiaxiazls/article/details/50118161

主要的意思就是,那个temp_ans是局部变量,会在for循环中一个循环结束之后被析构的,然而,set_allocated_answer中放进去的是这个局部变量的指针。所以,后面这个rsp使用的时候,里面的detail的answer的指针已经指向了非法的内存空间了。coredump妥妥的。

如果需要使用set_allocated这种方法,参数里面的指针需要指向一个在使用到这个数据的时候还没有被析构的对象(好绕口)。例如,那个指针参数是new 出来的,或者是在一个更外层的嵌套中定义的。但是,protobuf中会负责析构这个传进去的指针指向的对象,所以,最好传进去一个new出来的对象的地址,因为如果传进去一个原来就定义好的对象的地址,可能因为这个析构,而导致原有对象失效,造成一些潜在的问题(set_allocated_xxx中的allocated已经表明,最好是那种分配出来的对象)

像上面的例子,进行复合类型的赋值,用copyFrom比较好,例子如下:

Rsp rsp;
for(int index = 0;index<info.answer_size();index++){    
    Detail * detail = rsp.add_detail();    
    Answer* ans = detail->mutable_answer();    
    Answer temp_ans = info.answer(index);    
    ans->copyFrom(temp_ans);
}//do something with rsp


--------------------- 
 

 

  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Protobuf(Protocol Buffers)是一种轻量级的数据序列化格式,由Google开发。它可以用于结构化数据的序列化,用于数据通信、持久化和配置文件等场景。下面是使用protobuf的一般步骤: 1. 定义消息类型:使用protobuf语言定义文件(.proto)来描述数据结构和消息类型。你可以定义消息字段的名称、类型和规则等。 2. 编写.proto文件:在.proto文件中定义消息类型、字段和其他相关信息。例如,你可以定义消息的名称、字段的名称和类型、字段的规则(如必填、可选或重复)等。 3. 编译.proto文件:使用protobuf编译器将.proto文件编译为你所选编程语言的源代码。protobuf支持多种编程语言,如C++、Java、Python等。编译后会生成对应语言的源代码文件,其中包含与消息类型相关的类或结构体。 4. 在代码中使用protobuf:在你的代码中引入生成的源代码文件,并使用其中定义的类或结构体。你可以根据需要创建、修改和序列化protobuf消息,以及将其转换为二进制格式或其他格式。 5. 序列化和反序列化:使用protobuf库提供的方法将protobuf消息序列化为二进制格式,或者将二进制数据反序列化为protobuf消息。这样可以实现消息的传输和存储。 总结来说,使用protobuf可以实现跨语言、高效的数据序列化和反序列化,简化了数据传输和存储的过程。通过定义和编译.proto文件,并在代码中使用生成的源代码文件,你可以方便地使用protobuf进行数据处理。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值