Protobuf學習
google protobuf安裝與使用
ProtoBuf 官方文檔
[翻譯] ProtoBuf 官方文檔(二)- 語法指引(proto2)
[翻譯] ProtoBuf 官方文檔(三)- 語法指引(proto3)
[翻譯] 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
---------------------