一、先搞懂:Protobuf 編解碼的核心目標
- 編碼(序列化):將結構化數據(如對象、結構體)轉換成緊湊的二進制字節流,便於存儲 / 傳輸;
- 解碼(反序列化):將二進制字節流還原為原始的結構化數據。
二、Protobuf 編解碼的前置基礎:.proto 文件與字段規則
protobuf
// 示例:定義一個用户信息的.proto文件
syntax = "proto3"; // 指定Protobuf版本(主流為proto3)
message User {
int32 id = 1; // 字段編號1,類型int32
string name = 2; // 字段編號2,類型string
repeated int32 tags = 3; // 字段編號3,重複字段(數組)
}
三、核心原理 1:編碼(序列化)—— 如何把數據轉二進制?
1. 核心組成:Tag + Length(可選) + Value
|
部分
|
作用
|
|
Tag
|
字段編號 + 字段類型(wire type),用「可變長度整數(Varint)」編碼
|
|
Length
|
僅針對 “長度不固定” 的類型(如 string、bytes、嵌套 message),表示 Value 的字節數
|
|
Value
|
字段的實際值,按對應類型的編碼規則轉換為二進制
|
2. 關鍵編碼規則(新手必懂)
(1)Tag 編碼:字段編號 + Wire Type
|
Wire Type
|
含義
|
適用 Protobuf 類型
|
|
0
|
Varint(可變長整數)
|
int32/int64/bool/enum
|
|
2
|
長度分隔
|
string/bytes/repeated/ 嵌套 message
|
(2)Varint 編碼(核心優化點)
- 每個字節的最高位(第 8 位):1 表示後續還有字節,0 表示最後一個字節;
- 低 7 位:存儲數字的二進制值,按 “小端序” 拼接。
- 二進制:
10010110→ 拆分為10010110(最高位 1) +00000001(最高位 0); - Varint 編碼結果:
0x96 0x01(僅 2 字節,比固定 4 字節節省 50% 空間)。
(3)長度分隔類型編碼(如 string)
3. 完整編碼示例(以 User {id:1, name:"Tom"} 為例)
plaintext
// 步驟1:編碼id字段(字段編號1,Wire Type 0)
Tag計算:(1 << 3) | 0 = 8 → Varint編碼8 → 0x08
Value:1 → Varint編碼1 → 0x01
id字段編碼結果:0x08 0x01
// 步驟2:編碼name字段(字段編號2,Wire Type 2)
Tag計算:(2 << 3) | 2 = 18 → Varint編碼18 → 0x12
Length:"Tom"的UTF-8字節數=3 → Varint編碼3 → 0x03
Value:"Tom"的UTF-8字節 → 0x54 0x6F 0x6D
name字段編碼結果:0x12 0x03 0x54 0x6F 0x6D
// 最終完整二進制(拼接所有字段):
0x08 0x01 0x12 0x03 0x54 0x6F 0x6D
四、核心原理 2:解碼(反序列化)—— 如何還原二進制?
- 右移 3 位 → 字段編號;
- 取最後 3 位 → Wire Type;
- Wire Type=0(Varint):繼續讀取字節直到最高位為 0,拼接為整數;
- Wire Type=2(長度分隔):先讀取 Varint 得到 Length,再讀取 Length 個字節作為 Value;
- 讀取 0x08 → Varint 解析為 8 → 字段編號 1,Wire Type 0;
- 讀取 0x01 → Varint 解析為 1 → id=1;
- 讀取 0x12 → Varint 解析為 18 → 字段編號 2,Wire Type 2;
- 讀取 0x03 → Length=3 → 讀取後續 3 字節 0x54 0x6F 0x6D → 轉字符串 "Tom";
- 最終還原 User {id:1, name:"Tom"}。
五、Protobuf 編解碼的核心優勢(對比 JSON/XML)
|
特性
|
Protobuf
|
JSON
|
|
存儲格式
|
二進制
|
文本
|
|
字段標識
|
數字編號(佔 1-2 字節)
|
字符串字段名(佔多字節)
|
|
整數存儲
|
Varint(小數字省空間)
|
十進制字符串(如 "150")
|
|
解析速度
|
快(二進制直接解析)
|
慢(需解析文本 / 轉義字符)
|
|
版本兼容性
|
強(字段編號不變即可)
|
弱(字段名變更則失效)
|
|
可讀性
|
差(二進制)
|
好(人類可讀)
|