幻林

导航

关于数据传输格式的序列化和反序列化

我现在的工作中,有一件事情一直烦着我,那就是公司的序列化协议文件的编写,我感觉这是一件非常简单的事情,但是为什么光是一个*.h文件就要27916行代码,而*.cpp文件就需要66921行代码。

我来简单地写一下公司的协议定义:

.h头文件
struct Person:
{
    Person();
    ~Person()
    {
        ReleaseMemory();
    };
    
    AnalyzeMsgHead(char* thebuf);
    int FillBuf(char*& thebuf);
    void AnalyzeBuf(char* thebuf, int msgsize);
    void ReleaseMemory()
    {
    };

 Public:  
   string name;
   string age;
   string address;
   string phoneNumber; 
        
private:
   int GetMyMemSize();
};
.cpp文件
 Person::Person()
 {
    name = "";
    age= "";
    address= "";
    phoneNumber= "";
 }

void Person:AnalyzeBuf(char* thebuf, int msgsize)
{
    int theBufContent = 0;
    //name    
    name    =  thebuf+theBufContent;
    theBufContent += name.length()+1; 

    //age
    age = thebuf+theBufContent;
    theBufContent += age.length()+1;     

    //address
    address = thebuf+theBufContent;
    theBufContent += address.length()+1; 

//phoneNumber
     phoneNumber = thebuf+theBufContent;
     theBufContent += phoneNumber .length()+1; 
}

int Person::FillBuf(char*& thebuf)
{
    thebuf = new char[GetMyMemSize()];    
    //name
    memcpy(thebuf+theBufContent, name.c_str(), name.length()+1);
    theBufContent += name.length()+1;

    //age
    memcpy(thebuf+theBufContent, age.c_str(), age.length()+1);
    theBufContent += age.length()+1;

    //address
    memcpy(thebuf+theBufContent, address.c_str(), address.length()+1);
    theBufContent += address.length()+1;

    //phoneNumber
    memcpy(thebuf+theBufContent, phoneNumber.c_str(), phoneNumber.length()+1);
    theBufContent += phoneNumber.length()+1;

    return theBufContent;
}

int Person::GetMyMemSize()
{    
    memSize += name.length()+1;            //name
    memSize += age.length()+1;                //age
    memSize += address.length()+1;            //address
    memSize += phoneNumber.length()+1;        //phoneNumber

    return memSize;
}

  好吧,以上是完全的C语言风格的定义,随随便便定义一个协议就几十行的代码。如果定义vector、map这些类型的,那就更复杂了,如果加上嵌套的话,比如:

vector<vector<vector<int>>> 基本上是要人命。

     我一直想着各种法子修改这段协议代码,力求简单、易读,可以维护,我尝试了很多其他人的方法,我一一列举吧。

一、MFC的CArchive类

来看这篇文章http://blog.csdn.net/yestda/article/details/17177097 ,一篇关于CArchive的使用方法。

但是我觉得如果真用CArchive来写的话,未来肯定又是一个坑。我只是比较喜欢CArchive的读写方式,非常简单,如下:

二、使用google的protobuf

   一篇关于protobuf写得比较好的文章:

    http://blog.csdn.net/majianfei1023/article/details/45112415/

   通过这篇文章可知,要使用protobuf,那也不是一件非常简单的事情,要做一些我个人觉得不喜欢的事情:

  (1)下载protobuf源码

  (2)编译源代码

  (3)定义一个proto person.proto

  (4)生成目标语言c++格式。(生成了person.pb.hperson.pb.cc的文件

    其实说白了protobuf这东东就只是定义自己的数据结构,然后使用代码生成器生成的代码来读写这个数据结构。 这就说明了我必须要有.proto文件,否则读不出任何数据。

 相对我的项目而言,还是很麻烦。

   那我为什么不用json或xml,就我个人而已,json和xml虽然简单、易读,但是有一点我不喜欢,就是数据体积很大,还不如我自己公司编写的。大家可以看《protobuf开发指南》的1.3节,protobuffer的体积普遍比xml小3-10倍

 我唯一喜欢的,是protobuf的序列化和解序列化方式,示例代码:

Person person;
person.set_name(XXXX);
person.set_age(23);
string sOrder;
person.SerailzeToString(&sOrder); //这就将数据序列化到 sOrder里面了。
--------------------------------------------------------------------

Person person2;
if(person2.ParseFromString(sOrder))  //从字符串sOrder中提取数据
{
  cout << "name:" << person2.name() << endl
          << "age:" << person2.age() << endl;
}
else
{
  cerr << "parse error!" << endl;
}
---------------------------------------------------------------------

相对我公司的代码,是非常非常的简洁的。

参考了carchive和protobuf,但它们相对于我公司的项目,都没有什么实用性。

即使我改为其中的任何一种,我的那些同事都不会接受的,因为他们很懒,习惯了一个东西就很难改变。

我觉得需要写出另一种更为简单、便捷的协议定义方式,起码能让我的同事接受。

看了别人的“轮子”,那么我说一下我心中理想的的使用方式:  

#include <iostream>
#include "Serialize.h"

using namespace std;
struct MyStruct
{
public:
    void toString(string &str)
    {
        CSeriralize serial;
        serial << a << b << _char << str1 << vec_ini << vec_vec_int;
        str = serial.buffer();
    }

    void unString(const string &str)
    {
        CSeriralize serial(str);
        serial >> a >> b >> _char >> str1 >> vec_ini >> vec_vec_int;
    }
public:
    int a;
    int b;
    char  _char;
    string str1 ;
    vector<int> vec_ini;
    vector<vector<int>> vec_vec_int;
};

int _tmain(int argc, _TCHAR* argv[])
{
    MyStruct test;
    test.a = 1;
    test.b = 2;
    test._char = 'c';
    test.str1 = "fafafasf";
    test.vec_ini.push_back(1);
    test.vec_ini.push_back(2);
    test.vec_vec_int.push_back(test.vec_ini);
    test.vec_vec_int.push_back(test.vec_ini);

    string str;
    test.toString(str);

//--------------------------------------------------------------------------------------------
    MyStruct test1;
    test1.unString(str);
    getchar();
    return 0; 
}

就是说我需实现一个类: CSeriralize ,这样的话,我公司的代码至少减少百分之七十以上,因为.cpp文件可以完全去掉,也可以不重写那个几个函数。

 关于这个类的实现:https://github.com/wanglinhai888/TestSerials

 现在只是初步实现了这个类,提供一种思路,而内存保护啊什么的都没有弄,未来还会加入支持map、set、list 等方式,支持大小端,支持跨平台等等等。

posted on 2016-12-27 17:17  幻林  阅读(281)  评论(0编辑  收藏  举报