文章目錄

目前在项目中通信协议的封装用的都是google protocol buffer,本来也没什么问题,效率高而且也挺方便使用的,但是用久了就发现这种传统的使用方法很大的降低了工作效率。
我们的使用方式是,在.proto文件定义消息格式,通过protoc.exe生成.pb.h和.pb.cc文件,然后直接引用这些生成的文件来打包和解析消息。在我们项目中改动最频繁的就是这些消息协议文件了,而这些文件一旦改变整个项目就需要重新编译(消息文件几乎在所有模块里都要被引用),前期项目小的时候还无所谓,但后期积累的模块就太多了,重新编译要等个十来分钟,而这仅仅是因为改变了一个消息的定义才引发的。
某些时刻你会发现这十来分钟也是难以忍受的,所以我就想着怎么才能在协议改变的时候也不需要重新编译这个项目,而且仅仅编译有限的几个文件来解决问题。

在之前的博客里已经提到过protocol buffer动态解析的问题,通过这种技巧可以直接读取proto文件,获得消息的定义,最后从字节流中解析出消息内容,中间省略了.pb.h和.pb.cc文件的生成过程,从而避免了文件的大规模重新编译。(请参阅http://sstask.com/2015/09/08/pbdynamicananlysis/)
大体的流程是这样的:

通过google::protobuf::compiler::Importer加载proto文件,获取FileDescriptor
通过Importer->pool()->FindMessageTypeByName获取Descriptor
通过Descriptor获取Message
通过Message获取FieldDescriptor
最后通过FieldDescriptor读取或者写入filed的数值。

具体的操作方法在之前的博客里也写的比较清楚了,剩下的要做的也只是封装的问题,提供一个比较完善且便于使用的接口。
如果.proto文件内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
syntax = "proto3";
message mytag
{

int32 tag= 1;
}
message mymsg
{

int32 len = 1;
repeated int32 typ = 2;
map<uint32,string> str = 3;
mytag tag = 4;
}

我定义了一个gpbmessage类,直接如下调用构造函数就可以生成一个消息

1
gpbmessage gms("mymsg");

写入消息内容如下(可以写入vector和map,vector对应protocolbuffer里的repeated类型,map对应pb3.0里的map)

1
2
3
4
5
6
7
8
9
gms.setval("len", 2);
std::vector<int> vecs;
for(int i = 0; i < 10; ++i)
vecs.push_back(i);
gms.setrepeatedval("typ", vecs);
std::map<int,std::string> mapmy;
mapmy[1]="1";
mapmy[2]="22";
gms.setmap("str",mapmy);

message类型的写入(repeated和map类型读取和上面一致)

1
2
3
gpbmessage tag("mytag");
tag.setval("tag",1);
gms.setval("tag",tag);

流化消息和protocol buffer原有方式一致:

1
2
std::string out;
gms.serializetostring(&out);

读取并生成消息

1
gpbmessage gms1("mymsg", out);

读取消息内容

1
2
3
4
5
6
7
8
9
int val;
assert(gms1.getval("len", val));
std::vector<int> vecs1;
assert(gms.getrepeatedval("typ", vecs1));
std::map<int,std::string> mapmy1;
assert(gms.getmap("str", mapmy1));

gpbmessage tag();
assert(gms.getval("tag",tag));

可以看到上面这些操作不但囊括了protocol buffer里的所有类型,而且也使得repeated类型和map类型的操作更加简单方便。这里把原生的repeated类型和map类型转换成了stl中的vector和map进行处理,虽然损失了一定的效率,但是操作更加方便快捷。
在下篇博客里我将会讲讲protocol buffer中repeated类型和map类型的处理方法。
有兴趣可以直接看代码,上代码
https://github.com/sstask/googleprotobufmore/blob/master/gpbmessage.h

文章目錄