四.結(jié)構(gòu)體(message)
Protobuf的Message結(jié)構(gòu)是由一系列的key-value對組成的。其中,key由兩部分組成,成員序號field_number
和value的類型wire_type
,他們通過(field_number
<< 3) | wire_type
的方式編碼為一個varint;value根據(jù)wire_type而定;
Message結(jié)構(gòu)由.proto文件定義,這是協(xié)議雙方的接口文件,文件里面定義了每個結(jié)構(gòu)成員的序號即field_number
,以及每個成員的類型(該類型會映射到wire_type),例如:
message MSG_Login_Rsp
{
required 1:uint32 rspcode = 100;//
返回值,1為默認(rèn)值
required 2:string rspdesc =
"success"; //success
為默認(rèn)值
}
其中,rspcode的序號為1,類型為uint32,默認(rèn)值為100;rspdesc的序號為2,類型為string,默認(rèn)值為success。
下面我們來看如何根據(jù)proto文件序列化結(jié)構(gòu)體(proto文件的規(guī)則后續(xù)的文章中會說明,這里只是原理性的描述)。
首先,需要做成員類型的劃分,如下:
Type
|
含義
|
使用場景
|
0
|
Varint
|
uint8,uint16,uint32
|
2
|
Length-delimited
|
String
,bytes,embedded
messages,repeated fields
|
說明1)embedded message為結(jié)構(gòu)體的嵌套類型
2)repeated fields為某個可能重復(fù)的字段,重復(fù)次數(shù)可以為0
3)Length-delimited的含義是“長度+值”,其中長度采用varint編碼,值為字符序列或者二進(jìn)制碼流
4)Type的含義就是前文提到的wire_type
1)以uint32為例,假若有如下proto接口
message
Test1
{
required
1: uint32 a = 150;
}
查看序列化的結(jié)果,可以得到16進(jìn)制:
08 96 01
由于uint32類型較為簡單,我們?nèi)菀椎贸觯?/span>key為8,其中wire_type
為0,field_number為1,value為150
2)以string字符串為例,假若有如下proto接口
message
Test2
{
required
2: string msg = “testing”;
}
此時如果序列化,我們回得到下面16進(jìn)制碼流:
12
07 74 65 73 74 69 6e 67
剛才已經(jīng)提到message的編碼都是key-value對,OK,我們對號入座,由于key是一個varint,我們很容易斷定12就是key,將12轉(zhuǎn)化為二進(jìn)制格式:0001
0010,得出:wire_type
為2,field_number為2,說明接下來會出現(xiàn)
Length-delimited類型,根據(jù)“長度+值”的規(guī)則,推斷出7為string的長度,string為“testing”。
3)以結(jié)構(gòu)體的嵌套(embedded message)為例,假若有如下proto接口
message
Test3
{
required
3: Test1;
}
看序列化碼流:
1a 03 08 96 01
參考上面string的情況,我們得到:key為1a,其中wire_type
為2,field_number為3,value的為 08 96 01,注意此時value本身是一個key-value對,這個對我們在1)中已經(jīng)做了解析。
下一篇文章會描述proto接口文件的規(guī)則(主要基于libprotobuf的規(guī)則,但又有簡化)