首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C++ >> 關於C++

Google Protobuf 網路傳輸方案:自動反射訊息類型的編碼與解碼

     編輯:關於C++

解決核心問題:接收 Protobuf 資料後,如何自動建立具體 Message 物件並反序列化。「自動」意指新增訊息類型時無需修改此部分程式碼、無需手動註冊。Protobuf 本身具備強大反射功能,可根據型別名稱建立具體 Message 物件,直接利用即可。

網路傳輸使用 Protobuf 的兩大問題

  1. 長度: Protobuf 無自帶長度資訊,需應用層自行分包(通常加固定長度長度前綴)
  2. 類型: Protobuf 無自帶類型資訊,需發送方傳遞類型,接收方據以建立具體 Message 物件

常見山寨做法及其缺陷

  • int typeId: 需維護全域唯一性,跨部門協作需中央分配機制
  • string typeName + lookup table: 唯一性較易(可用 fully qualified name),但每新增類型需手動修改查找表初始化碼

Protobuf 內建反射機制:正確解法

核心類別關係

  • Descriptor:每個具體 Message 類型對應一個全域 Descriptor 物件
  • DescriptorPool:可根據型別名稱查找 Descriptor*
  • MessageFactory:可根據 Descriptor* 取得 prototype(default instance)
  • Message::New():原型模式,從 prototype 複製新物件

關鍵不變式驗證

const Descriptor* descriptor = DescriptorPool::generated_pool()->FindMessageTypeByName(type_name);
assert(descriptor == ConcreteMessage::descriptor());

const Message* prototype = MessageFactory::generated_factory()->GetPrototype(descriptor);
assert(prototype == &ConcreteMessage::default_instance());

Message* new_obj = prototype->New();
assert(typeid(*new_obj) == typeid(ConcreteMessage::default_instance()));

自動建立 Message 核心程式碼

Message* createMessage(const std::string& typeName) {
    Message* message = NULL;
    const Descriptor* descriptor = DescriptorPool::generated_pool()->FindMessageTypeByName(typeName);
    if (descriptor) {
        const Message* prototype = MessageFactory::generated_factory()->GetPrototype(descriptor);
        if (prototype) {
            message = prototype->New();
        }
    }
    return message; // 呼叫端負責釋放,muduo 中用 shared_ptr 管理
}

自訂 Protobuf 傳輸格式

struct ProtobufTransportFormat __attribute__ ((__packed__)) {
    int32_t len;              // 總長度
    int32_t nameLen;          // 類型名長度
    char typeName[nameLen];   // 類型名 (含結尾 \0)
    char protobufData[len-nameLen-8]; // Protobuf 序列化資料
    int32_t checkSum;         // Adler32(nameLen + typeName + protobufData)
};

設計決策理由

  • signed int: 相容 Java 無 unsigned 型別,Protobuf 通常 <1MB
  • Adler32 校驗: 計算量小、速度快、強度近似 CRC32、zlib/Java 內建
  • typeName 以 \0 結尾 + nameLen: 便於 tcpdump 肉眼偵錯,空間換時間避免 strlen
  • 無版本號: Protobuf 以 optional fields 演進協議,版本號屬畫蛇添足

編碼/解碼範例

完整程式碼:

訊息分發器

利用 Descriptor 實現兩種分發器:

  • ProtobufDispatcherLite: 需手動 down_cast
  • ProtobufDispatcher: 模板技巧自動 down_cast,節省使用者程式碼

後續規劃

  • 整合至 muduo (預計 0.1.9 版),支援 ProtobufMessage <=> muduo::net::Buffer 雙向轉換
  • 提供 Codec 元件,應用層直接收發 Message 物件
  • 基於 muduo 的 Protobuf RPC 探討
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved