protobuf 数据解析的2种方法

来源:互联网 发布:linux sacc 编辑:程序博客网 时间:2024/05/21 10:50
方法1:


message person
{
required int32 age = 1;
required int32 userid = 2;
optional string name = 3;
}


 


message test
{
required int32 time = 1;
required int32 userid = 2;
required float price = 3;
optional string desc = 4;
}

#include <string>#include <iostream>#include <assert.h>#include <stdint.h>#include "person.pb.h"#include "test.pb.h"using namespace std;class ProtoMsgHandle{public:    /*  注册消息处理函数 */    void    initHandles()    {        registerHandle(&ProtoMsgHandle::handleProtoPerson);        registerHandle(&ProtoMsgHandle::handleProtoTest);    }    /*  处理网络消息     *  data 为一个完整的数据包     */    void    handle(const char* data)    {        bool ret = false;        const char * current=data;        //在网络上传输的一个数据包总长度        int packetLength=0;        //从第一个位置上获取到数据包总长度        memcpy(&packetLength, data, sizeof(int32_t));        //指针后移        current+=sizeof(int32_t);        //Message名字的长度        int protoNameLength=0;        //从第二个位置上获取Message的名字的长度        memcpy(&protoNameLength, current, sizeof(int32_t));        //指针后移        current+=sizeof(int32_t);        //从第三个位置上获取Message的名字        string name(current,protoNameLength);        //指针后移        current+=protoNameLength;        //取得Message的字节数        int messageSize=packetLength-(sizeof(int32_t)+sizeof(int32_t)+protoNameLength);        do{            msg_handle callback = m_callbacks[name];            assert(callback != NULL);            if(callback == NULL)            {                std::cout<<"proto "<<name<<" had not register handler"<<std::endl;                break;            }            const ::google::protobuf::Descriptor* descriptor = m_descriptors[name];            assert(descriptor != NULL);            if(descriptor == NULL)            {                std::cout<<"proto "<<name<<" had no descriptor"<<std::endl;                break;            }            const google::protobuf::Message* prototype = google::protobuf::MessageFactory::generated_factory()->GetPrototype(descriptor);            assert(prototype != NULL);            if(prototype == NULL)            {                std::cout<<"proto "<<name<<" had no prototype"<<std::endl;                break;            }            google::protobuf::Message* msg = prototype->New();            ret = msg->ParseFromArray(current,messageSize);            if(ret)            {                (this->*callback)(msg);            }            else            {                std::cout<<"proto "<<name<<" parse fail"<<std::endl;            }        }while(0);    }private:    void handleProtoTest(test* test)    {        cout <<"test->price()="<< test->price() << endl;        cout << "test->userid()="<<test->userid() << endl;        cout << "test->time()="<<test->time() << endl;    }    void handleProtoPerson(person* person)    {        cout << "person->age()="<<person->age() << endl;        cout << "person->userid()="<<person->userid() << endl;        cout << "person->name()="<<person->name() << endl;    }private:    typedef void (ProtoMsgHandle::*msg_handle)(::google::protobuf::Message*);    map<string, msg_handle>                                 m_callbacks;    map<string, const ::google::protobuf::Descriptor*>      m_descriptors;    template<typename MSGTYPE> void registerHandle(void (ProtoMsgHandle::*callback)(MSGTYPE*))    {        const ::google::protobuf::Descriptor*des =MSGTYPE::descriptor();        assert(des != NULL);        if(des != NULL)        {            m_callbacks[des->full_name()] = (msg_handle)callback;            m_descriptors[des->full_name()] = des;        }    }};class ProtoMessageSender{public:    /*  发送proto msg到指定缓冲区     *  int32_t   packetLength 数据包总长度     *  int32_t   messageNameLength 消息名长度     *  char[]    messageName 消息名     *  char[]    Message 消息     *  char*     buffer 缓冲区     *  int       maxLength 消息的最大长度     */    template<typename MSGTYPE> static int sendMessageToBuffer(MSGTYPE& msg, char* buffer, int maxLength){        char * current=buffer;        //Message的字节数        int messageSize=msg.ByteSize();        //Message的名字        string messageName=MSGTYPE::descriptor()->full_name();        //Message名字的长度        size_t messageNameLength=messageName.size();        //消息组成 messageSize+messageNameLength+messageName+Message        size_t packetLength=sizeof(int32_t)+sizeof(int32_t)+messageNameLength+messageSize;        if (packetLength>maxLength) {            return -1;        }        //将数据包总长度放在第一个位置        memcpy(current, &packetLength, sizeof(int32_t));        //指针后移        current+=sizeof(int32_t);        //将Message名称长度放在第二个位置        memcpy(current, &messageNameLength, sizeof(int32_t));        //指针后移        current+=sizeof(int32_t);        //将协议名称放在第三个位置上        strcpy(current,messageName.c_str());        //指针后移        current+=messageNameLength;        //将Message放在第四个位置上        msg.SerializeToArray(current,messageSize);        return (int)packetLength;    }};int main(){    ProtoMsgHandle msghandle;    msghandle.initHandles();    test t;    t.set_price(100.0);    t.set_userid(110);    t.set_time(123);    person person;    person.set_age(18);    person.set_userid(200508);    person.set_name("irons");    char tmp[10*1024];    ProtoMessageSender::sendMessageToBuffer(t, tmp, sizeof(tmp));    msghandle.handle(tmp);    ProtoMessageSender::sendMessageToBuffer(person, tmp, sizeof(tmp));    msghandle.handle(tmp);    cin.get();    return 0;}


 方法2:


http://my.oschina.net/cxh3905/blog/159122


 


 


比较:


方法1把每种消息注册到链表中,当有消息来时,迭代处理,每个消息对应一个回调函数。


方法2只有1种消息,里面有个成员msg type,根据这个type做相应的处理。


前者更直观,结构清晰,每个消息占用空间合理,但效率地下。


后者效率更高,但所有数据混杂在一起,代码阅读性差。