解決核心問題:接收 Protobuf 資料後,如何自動建立具體 Message 物件並反序列化。「自動」意指新增訊息類型時無需修改此部分程式碼、無需手動註冊。Protobuf 本身具備強大反射功能,可根據型別名稱建立具體 Message 物件,直接利用即可。
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* 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 管理
}
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)
};
完整程式碼:
利用 Descriptor 實現兩種分發器:
ProtobufMessage <=> muduo::net::Buffer 雙向轉換