一、简介
protocolbuffer(以下简称PB)是google 的一种数据交换的格式,它独立于语言,独立于平台。google 提供了三种语言的实现:java、c++ 和 python,每一种实现都包含了相应语言的编译器以及库文件。由于它是一种二进制的格式,比使用 xml 进行数据交换快许多。可以把它用于分布式应用之间的数据通信或者异构环境下的数据交换。作为一种效率和兼容性都很优秀的二进制数据传输格式,可以用于诸如网络传输、配置文件、数据存储等诸多领域。
1.1、关键字定义
gpb:google protocol buffer
1.2、首先感谢这些链接里的帮助:
http://www.searchtb.com/2012/09/protocol-buffers.html 【玩转gpb】
http://www.ibm.com/developerworks/cn/linux/l-cn-gpb/?ca=drs-tp4608
http://blog.csdn.net/soso_blog/article/details/6106495
http://www.blogjava.net/DLevin/archive/2015/04/01/424012.aspx
http://www.cnblogs.com/stephen-liu74/archive/2013/01/02/2841485.html
https://github.com/google/protobuf【github源码】
http://stackoverflow.com/questions/10277576/google-protocol-buffers-on-ios【步骤和问题】
1.3、为什么选择动态描述和动态解析
项目需要,动态牺牲了性能,代码一样支持静态。
二、相关操作步骤和注意事项
2.1 平台和语言
选用的是google C++源码编译和封装,目前这套代码在windows、android、IOS系统测试通过,只需要添加进去调用API即可。.cc文件都改成了cpp文件。开发之前建议看下玩转GPB里面的介绍,静态自描述和动态自描述和相关性能,编码什么的,以及整个gpb的类图结果,以上链接都有。至于不同的平台主要是在config.h里面做一些配置即可。
2.2 注意事项
android里面对于zip宏定义需要屏蔽,IOS里面主要是引入文件路径设置,libz.dylib库引入和map宏定义的选择(ios折腾了好几天,最后发现问题很简单...)。
2.3 相关代码
这个是windows下面基于过程式调用,静态创建和动态创建自描述相关代码(有点乱),主要提供一种思路。后面会提供封装好的API
// @@@@@@@@@@@@@@@@@@@@@@C过程式调用@@@@@@@@@@@@@@@@@@@@@@
/*
FileDescriptorProto *file_proto = new FileDescriptorProto;
file_proto->set_name("foo.proto");
// create dynamic message proto names "Pair"
DescriptorProto *message_proto = new DescriptorProto;
message_proto = file_proto->add_message_type();
message_proto->set_name("Pair");
FieldDescriptorProto *field_proto = NULL;
field_proto = message_proto->add_field();
field_proto->set_name("key");
field_proto->set_type(FieldDescriptorProto::TYPE_STRING);
field_proto->set_number(1);
field_proto->set_label(FieldDescriptorProto::LABEL_REQUIRED);
field_proto = message_proto->add_field();
field_proto->set_name("value");
field_proto->set_type(FieldDescriptorProto::TYPE_INT32);
field_proto->set_number(2);
field_proto->set_label(FieldDescriptorProto::LABEL_REQUIRED);
// add the "Pair" message proto to file proto and build it
DescriptorPool *pool = new DescriptorPool;
const FileDescriptor *file_descriptor = pool->BuildFile(*file_proto);
const Descriptor *descriptor = file_descriptor->FindMessageTypeByName("Pair");
//cout << descriptor->DebugString();
cout<DebugString();
// build a dynamic message by "Pair" proto
DynamicMessageFactory *factory = new DynamicMessageFactory(pool);
const Message *message = factory->GetPrototype(descriptor);
// create a real instance of "Pair"
Message *pair = message->New();
cout<DebugString();
getchar();
const Reflection *reflection = pair->GetReflection();
const FieldDescriptor *field = NULL;
field = descriptor->FindFieldByName("key");
reflection->SetString(pair, field, "my key");
std::string strRet = reflection->GetString(*pair, field);
cout<FindFieldByName("value");
reflection->SetInt32(pair, field, 1234);
//cout<DebugString();
//cout<DebugString();
cout<DebugString();
cout<DebugString();
*/
// @@@@@@@@@@@@@@@@@@@@@@静态调用@@@@@@@@@@@@@@@@@@@@@@
// lm::student stu1; // 静态测试通过 序列化和反序列化 校验
// stu1.set_id(1989);
// stu1.set_str("xuqiang");
/*
std::string transport = Encode(*pair);
//print(transport);
delete pair;
int32_t be32 = 0;
std::copy(transport.begin(), transport.begin() + sizeof(be32), reinterpret_cast(&be32));
int32_t len = ::ntohl(be32);
assert(len == transport.size() - sizeof(be32));
std::string buf = transport.substr(sizeof(int32_t));
// 反序列化
std::string transportEx = EncodeEx(*pFileProto, *pMessageRet);
// 释放内存【注意释放顺序】
DeletePtr(pMessageRet);
DeletePtr (pFactory);
DeletePtr (pFileProto);
DeletePtr (pPool);
// int32_t be32 = 0;
// std::copy(transportEx.begin(), transportEx.begin() + sizeof(be32), reinterpret_cast(&be32));
// int32_t len = ::ntohl(be32);
// assert(len == transportEx.size() - sizeof(be32));
//
// std::string buf = transportEx.substr(sizeof(int32_t));
// assert(len == buf.size());
// lm::student *newStu = dynamic_cast(Decode(buf));
// assert(newStu != NULL);
// newStu->PrintDebugString();
// delete newStu;
//Message *newMsg = dynamic_cast(Decode(buf));
std::string messageData;
Message *newMsg = dynamic_cast(DecodeEx(transportEx, messageData));
assert(newMsg != NULL);
// 查找
std::string findString = GetString(newMsg, messageData, "key");
//cout<PrintDebugString();
cout<DebugString();
delete newMsg;
*/
// 随便写的测试
// buf[buf.size() - 6]++;
// lm::student* badStu = dynamic_cast(Decode(buf));
// assert(badStu == NULL);
/*
// ==自描述消息=========================================================================================
int nID = 1234567890;
char szName[] = "Hello World";
// 服务端:把数据序列化到"log服务器"中
lm::student stu1;
stu1.set_id(nID);
stu1.set_str(szName);
fstream output;
output.open(_T("./log"), ios::out | ios::trunc |ios::binary);
stu1.SerializePartialToOstream(&output); // 序列号
output.close();
// 客户端:将"log服务器"中的数据反序列化
lm::student stu2;
fstream input("./log", ios::in);
stu2.ParseFromIstream(&input); // 反序列化
int theID = stu2.id(); // 读取数据
string theName = stu2.str(); // 读取数据
input.close();
cout << theID << endl;
cout << theName << endl;
// 网络发送
string strSend;
stu2.SerializePartialToString(&strSend);
// send(strSend);
// 网络接收
lm::student stSock;
if (stSock.ParseFromString(strSend))
{
cout<<"stSock.id = "<set_name("Pair");
FieldDescriptorProto *field_proto = NULL;
field_proto = message_proto->add_field();
field_proto->set_name("key");
field_proto->set_type(FieldDescriptorProto::TYPE_STRING);
field_proto->set_number(1);
field_proto->set_label(FieldDescriptorProto::LABEL_REQUIRED);
field_proto = message_proto->add_field();
field_proto->set_name("value");
field_proto->set_type(FieldDescriptorProto::TYPE_UINT32);
field_proto->set_number(2);
field_proto->set_label(FieldDescriptorProto::LABEL_REQUIRED);
DescriptorPool pool;
const FileDescriptor *file_descriptor = pool.BuildFile(file_proto);
const Descriptor *descriptor = file_descriptor->FindMessageTypeByName("Pair");
// 动态创建消息
DynamicMessageFactory factory(&pool);
const Message *message = factory.GetPrototype(descriptor);
// create a real instance of "Pair"
Message *pair = message->New();
*/
/*
// write the "Pair" instance by reflection
const Reflection *reflection = pair->GetReflection();
// 动态修改信息
const FieldDescriptor *field = NULL;
field = descriptor->FindFieldByName("key");
reflection->SetString(pair, field, "my key");
field = descriptor->FindFieldByName("value");
reflection->SetUInt32(pair, field, 1234);
cout<DebugString();
delete pair;
pair = NULL;
*/
/*
// ==动态自描述消息=========================================================================================
//
// 生产者和消费者商定文件格式如下:
//------------------------------------------------------------------------------------------------
//| uint32 magic | uint32 Length | FieldDescriptorProto | uint32 Length | string MessageData |
//------------------------------------------------------------------------------------------------
// 生成动态消息
// message pair {
// required string key = 1;
// required uint32 value = 2;
// }
// 生产者
FileDescriptorProto file_proto;
file_proto.set_name("foo.proto");
// create dynamic message proto names "Pair"
DescriptorProto *message_proto = file_proto.add_message_type();
message_proto->set_name("Pair");
FieldDescriptorProto *field_proto = NULL;
field_proto = message_proto->add_field();
field_proto->set_name("key");
field_proto->set_type(FieldDescriptorProto::TYPE_STRING);
field_proto->set_number(1);
field_proto->set_label(FieldDescriptorProto::LABEL_REQUIRED);
field_proto = message_proto->add_field();
field_proto->set_name("value");
field_proto->set_type(FieldDescriptorProto::TYPE_UINT32);
field_proto->set_number(2);
field_proto->set_label(FieldDescriptorProto::LABEL_REQUIRED);
DescriptorPool pool;
const FileDescriptor *file_descriptor = pool.BuildFile(file_proto);
const Descriptor *descriptor = file_descriptor->FindMessageTypeByName("Pair");
// const google::protobuf::Descriptor* descriptor =
// google::protobuf::DescriptorPool::generated_pool()->FindMessageTypeByName("Pair");
// 动态创建消息
DynamicMessageFactory factory(&pool);
const Message *message = factory.GetPrototype(descriptor);
// create a real instance of "Pair"
Message *pair = message->New();
// write the "Pair" instance by reflection
const Reflection *reflection = pair->GetReflection();
const FieldDescriptor *field = NULL;
field = descriptor->FindFieldByName("key");
reflection->SetString(pair, field, "my key");
field = descriptor->FindFieldByName("value");
reflection->SetUInt32(pair, field, 1234);
field = descriptor->FindFieldByName("key");
string strData1 = reflection->GetString(*pair, field);
field = descriptor->FindFieldByName("value");
int32 nValue = reflection->GetUInt32(*pair, field);
file_proto.set_name("foo.proto");
const unsigned int MAGIC_NUM=2988;
int fd = open("dpb.msg", O_WRONLY|O_CREAT,0666);
ZeroCopyOutputStream* raw_output = new FileOutputStream(fd);
CodedOutputStream* coded_output = new CodedOutputStream(raw_output);
coded_output->WriteLittleEndian32(MAGIC_NUM);
string data;
file_proto.SerializeToString(&data);
coded_output->WriteVarint32(data.size());
coded_output->WriteString(data);
data.clear();
//pair->SerializeToString(&data);
pair->AppendToString(&data);
coded_output->WriteVarint32(data.size());
coded_output->WriteString(data);;
delete coded_output;
delete raw_output;
delete pair;
close(fd);
// 消费者
FileDescriptorProto file_proto_Custum;
int fd2 = open("dpb.msg", O_RDONLY);
ZeroCopyInputStream* raw_input = new FileInputStream(fd2);
CodedInputStream* coded_input = new CodedInputStream(raw_input);
unsigned int magic_number;
coded_input->ReadLittleEndian32(&magic_number);
if (magic_number != MAGIC_NUM) {
cerr << "File not in expected format." << endl;
return 1;
}
uint32 size;
coded_input->ReadVarint32(&size);
char* text = new char[size + 1];
coded_input->ReadRaw(text, size);
text[size] = '\0';
file_proto_Custum.ParseFromString(text);
DescriptorPool pool_Custom;
const FileDescriptor *file_descriptor_C = pool_Custom.BuildFile(file_proto_Custum);
const Descriptor *descriptor_C = file_descriptor_C->FindMessageTypeByName("Pair");
// 创建消息
DynamicMessageFactory factory_C(&pool);
const Message *message_C = factory_C.GetPrototype(descriptor_C);
// create a real instance of "Pair"
Message *pair_C = message_C->New();
coded_input->ReadVarint32(&size);
text = new char[size + 1];
coded_input->ReadRaw(text, size);
text[size] = '\0';
pair_C->ParseFromString(text);
// 读取消息
delete text;
delete pair_C;
*/
getchar();
需要的把邮箱留起