关于数据传输格式的序列化和反序列化
我现在的工作中,有一件事情一直烦着我,那就是公司的序列化协议文件的编写,我感觉这是一件非常简单的事情,但是为什么光是一个*.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.h和person.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 等方式,支持大小端,支持跨平台等等等。