google protobuf初体验

来源:互联网 发布:js判断等于空字符串 编辑:程序博客网 时间:2024/05/16 15:25

google protobuf初体验

最近在读别人代码的时候发现一个的东西,名字叫protobuf, 感觉挺好用的,写在这里,留个记录。那么什么是protobuf 呢?假如您在网上搜索,应该会得到类似这样的文字介绍:

Google Protocol Buffer( 简称 Protobuf) 是 Google 公司内部的混合语言数据标准,目前已经正在使用的有超过 48,162 种报文格式定义和超过 12,183 个 .proto 文件。他们用于 RPC 系统和持续数据存储系统。

Protocol Buffers 是一种轻便高效的结构化数据存储格式,可以用于结构化数据串行化,或者说序列化。它很适合做数据存储或 RPC 数据交换格式。可用于通讯协议、数据存储等领域的语言无关、平台无关、可扩展的序列化结构数据格式。目前提供了 C++、Java、Python 三种语言的 API。

说的明白点,它其实是一个可以帮你生成自定义数据结构的代码,并提供了序列化该结构的方法,并且支持多语言,跨平台等一系列优点。多说无益,直接说说该怎么用它。

首先 你需要编写proto文件,在这个文件中 用简单的方法定义了你想要定义的数据结构的组成信息。比如定义一个helloworld的信息,里面包含name, password, email 这三个信息。那么你的proto文件应该是这个样子的:

1
2
3
4
5
6
7
package im;
message helloworld
{
  required string usrname = 1;
  required string passwd = 2;
  optional string email = 3;
}

 im 说明了包的名称, helloworld 说明了 具体的结构类型。 一个比较好的习惯是认真对待 proto 文件的文件名。比如将命名规则定于如下:packageName.MessageName.proto

接下来 你需要运行一个命令来来生成该数据结构的代码

假设您的 proto 文件存放在 $SRC_DIR 下面,您也想把生成的文件放在同一个目录下,则可以使用如下命令:

protoc -I=$SRC_DIR --cpp_out=$DST_DIR $SRC_DIR/im.helloworld.proto

那么这个时候你就会在DST_DIR下面看到im.helloworld.pb.h  im.helloworld.pb.cpp

此时基本就大功告成了,你就可以在你的代码里使用关于helloworld这个数据结构的一切了。

这个工具主要比较方便的地方是它给你提供的序列化工具。可以直接将一个数据结构序列化为字符、或者从字符反序列化为数据结构。你想,这就为很多的网络程序提供了方便,在客户机在向服务端传递消息的时候可以直接将需要传递的消息用一个数据结构封装,然后用protobuf提供的序列化方法,序列化为一个字符串,然后你人为的加一个包头(包含消息的长度和消息的类型)做一次encode;在服务端接收该消息的时候根据encode的方法对包进行接收,接收完之后再用protobuf提供的反序列化方法,从字符串再次还原为具体的数据结构信息。整个过程就不需要你去造轮子去做那些繁琐的字符解析拼装的工作了,是不是很爽? 这个我认为是protobuf比较有用的地方之一,另外一个比较有用的地方是,它可以根据类型信息去create对象,比如 之前我定义的helloworld结构,假如我现在在做一个服务端的程序,我想实现这样的一个功能,在接收到helloworld消息的时候,我去执行相应的helloworld回调。当然对于helloworld的回调是在系统启动的时候加载的。这样有一个好处,实现整个系统的模块化,并且耦合度也很低。没有这个工具的话,我可能就需要在解析的时候特别的关注收到的消息类型字段,然后根据该字段去new一个相应的对象,进而调用相应的处理过程,整个过程都需要你去手写,有了protobuf,你完全不需要造轮子了。

那么怎么样根据消息的类型直接创建相应的对象呢?代码如下:

复制代码
google::protobuf::Message* create_message(const std::string& type_name){  google::protobuf::Message* message = NULL;  const google::protobuf::Descriptor* descriptor = \  google::protobuf::DescriptorPool::generated_pool()->FindMessageTypeByName(type_name);  if(descriptor)  {    const google::protobuf::Message* prototype =      google::protobuf::MessageFactory::generated_factory()->GetPrototype(descriptor);    if(prototype)    {      message = prototype->New();    }  }  return message;}
复制代码

有点设计模式里工厂模式的味道。在消息的种类不多的时候,你可以感觉不到它的好处,但在你的系统越来越庞大的时候,你自然能体会到这个小小函数的甜头。

在你知道消息类型,并根据这个工具生成消息对象的时候,接下来就该处理了,也就是dispatch。上面我有提到过,你可以注册每个消息类型的回调处理方法,通俗点讲就是你的系统里需要有一个map,这个map里存储了不同消息的处理方法,类似于:

std::map<std::string, message_callback_t>  _message_callbacks; 这样的一个容器。具体的方法如下:

复制代码
void deal_message(google::protobuf::Message* msg){  auto iter_map = _message_callbacks.find(msg->GetTypeName()); //得到消息的具体类型,protobuf提供的内置方法  if(iter_map != _message_callbacks.end())  {    iter_map->second(msg);  //相应的回调处理  }  else  {    std::cout << "message dealer no found" << std::endl;  }
}
复制代码

你看这样是不是简直帅呆了,你的系统如果用这样的方法来执行消息分发处理,整个系统将会变得很清晰,模块间耦合很低。

基于以上的思路,我简单实现了一个client和server,来示例本博文的思路,源代码地址:https://github.com/xiaopeifeng/CodeTricks/tree/master/protobuf

欢迎指正批评。

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 管子断在水管里怎么办 衣服上的织带缩水怎么办 真丝衣服拔缝了怎么办 顾客说衣服太花怎么办 铝和碱反应变黑怎么办 40度高温多肉怎么办 沾到医用蓝药水怎么办? 裤子弄上泡沫胶怎么办 苍蝇纸粘衣服上怎么办 苍蝇胶沾衣服上怎么办 灯带为什么不亮怎么办 苹果6比屏幕变黄怎么办 雷腾键盘锁了怎么办 自吸泵电机不转怎么办 孕38周胎儿偏小怎么办 被火烧黑的铁怎么办 锅被烟熏黑了怎么办 墙壁被烟熏黑了怎么办 壁纸被烟熏黑了怎么办 空调被烟熏黑了怎么办 牙被烟熏黑了怎么办 尖头鞋把尖折了怎么办 腰椎固定手术钢钉断了怎么办 脚被钢钉扎了怎么办 皮帘子有胶了怎么办 12v插口没有电怎么办 吃了一颗聚乙烯醇怎么办 立式注塑机产品粘膜怎么办 被小松鼠咬了怎么办 被宠物松鼠咬了怎么办 手被松鼠咬出血怎么办 银联认证码失败怎么办 国际汇款触发合规查询怎么办 外面的网线断了怎么办 网线被别的车挂断了怎么办 施工挖断军用光缆怎么办 不小心挖断光缆怎么办 家里的光纤断了怎么办 车被树枝刮花了怎么办 货车撞断了树枝怎么办? 把光缆挖断了怎么办