文档章节

Google Protocol Buffers 学习笔记

yejq8
 yejq8
发布于 2015/05/17 16:50
字数 1487
阅读 210
收藏 1
点赞 0
评论 0

Google Protocol Buffers 学习

Protocol BuffersPB)是一个用于序列化结构化数据的机制,是谷歌的一个开源项目,在github上有源代码,也有发行版。PBXML相似,XML序列化的时候速度是可以接受的,但是XML解析数据的时候非常慢。然而Protocol Buffers则是非常轻量,速度也很快。

 

我们学习Protocol Buffers,直接通过实现一个Protocol Buffers小项目来实验,得出其源代码,再分析编码技术。

安装完Protocol Buffers之后(安装过程略去,我们选择的是Github上面的源代码c++版,而不是发行版,有利于我们学习编码技术),我们开始正式实验。

 

关于PB的编码

由官方文档,PB先用variant编码整形数据,然后再进行操作

我们打开编译好的源代码我们找到了这样一条在序列化的时候所调用的函数

  if (has_sid()) {
    ::google::protobuf::internal::WireFormatLite::WriteInt32(2, this->sid(), output);
  }


使用gdb调试,尝试进入函数体内部,发现其调用:

src/google/protobuf/message.cc:174 的一个函数

bool Message::SerializeToOstream(ostream* output) const

最后找到了编码的函数:

src/google/protobuf/coded_stream.cc

inline uint8* CodedOutputStream::WriteVarint32FallbackToArrayInline(
    uint32 value, uint8* target) {
  target[0] = static_cast<uint8>(value | 0x80);
  if (value >= (1 << 7)) {
    target[1] = static_cast<uint8>((value >>  7) | 0x80);
    if (value >= (1 << 14)) {
      target[2] = static_cast<uint8>((value >> 14) | 0x80);
      if (value >= (1 << 21)) {
        target[3] = static_cast<uint8>((value >> 21) | 0x80);
        if (value >= (1 << 28)) {
          target[4] = static_cast<uint8>(value >> 28);
          return target + 5;
        } else {
          target[3] &= 0x7F;
          return target + 4;
        }
      } else {
        target[2] &= 0x7F;
        return target + 3;
      }
    } else {
      target[1] &= 0x7F;
      return target + 2;
    }
  } else {
    target[0] &= 0x7F;
    return target + 1;
  }
}

@font-face {   font-family: "Times New Roman"; }@font-face {   font-family: "宋体"; }@font-face {   font-family: "Liberation Serif"; }@font-face {   font-family: "Mangal"; }p.p0 { margin: 0; font-size: 16px; font-family: "Liberation Serif"; }div.Section0 { page: Section0; }

variant编码中第一个字节的高位msb1表示下一个字节还有有效数据,msb0表示该字节中的后7为是最后一组有效数字。踢掉最高位后的有效位组成真正的数字。

 

如数据value = 123456 运行上述程序的过程:

12345D = 3039H

target[0] = u8(3039H | 80H)       B9

30B9H > 80H

target[1] = 30B9H >> 7        60H

61H < 8000H

target[1] &= 0x7F   60H



结果应该是:B961H = 1011 1001 0110 0000 B

低位是的元数据位是1,表示后面还有8位的数据,依次下去

按照规则解析过来,刚好就是12345D

int32的编码

用上述方法使用gdb调试程序,得到函数入口

1、 计算长度1 + Int32Size(value);

(gdb) 

google::protobuf::io::CodedOutputStream::VarintSize32 (value=)

    at /usr/local/include/google/protobuf/io/coded_stream.h:1108

2、 调用这个函数,将值写入新的空间之中去

(gdb) step

google::protobuf::internal::WireFormatLite::Int32Size (value=)

    at /usr/local/include/google/protobuf/wire_format_lite_inl.h:797

存储的时候分为tagvalue两块。tag部分由公式field_number << 3 | WITETYPE_VARIANT给出,

value部分则由用户给的值的variant编码构成

 

String类型的编码

同样需要使用variant编码,string的要求是UTF8的编码的。

先计算计算长度公式 1 + variant(stringLength)+stringLength

编码后的格式为tag+length(variant int)+value

tage为公式field_number << 3 | WITETYPE_VARIANT给出,length则是长度的variant编码,value为用户字符串内容

函数:

(gdb) 

google::protobuf::internal::WireFormat::VerifyUTF8StringFallback (

    data=0x613118 "ye jiaqi", size=8, 

    op=google::protobuf::internal::WireFormat::SERIALIZE, 

    field_name=0x407dc8 "Lab.Student.name")

    at google/protobuf/wire_format.cc:1089

1089                                           const char* field_name) {

 

下面进行实操:

 

定义一个Protocol Buffers

package Lab;
message Student{
required string name = 1;
required int32 sid = 2;
required int32 age = 3;
enum genderType {
FEMALE = 0;
MALE = 1;
}

required genderType gender = 4;
optional string phone = 5;
optional string email = 6;
}


然后使用protoc将其编译为c++

 写一个简单的脚本测试这个类


#include <iostream>
#include <fstream>
#include <string>
#include "student.pb.h"
 
using std::cin;
using std::cout;
using std::cerr;
using std::endl;
using std::string;
using std::fstream;
using std::ios;
 
void setStudent(Lab::Student * student) {
  cout << "Enter the student's name" << endl;;
  string name;
  getline(cin, name);
  student->set_name(name);
 
  cout << "Enter the student's id" << endl;
  int id;
  cin >> id;
  student->set_sid(id);
 
  cout << "Enter the student's age" << endl;
  int age;
  cin >> age;
  student->set_age(age);
  
  cout << "Enter the student's gender 'male' or 'female'" << endl;
  string gender;
  cin.ignore(256, '\n');
  getline(cin, gender);
  if(gender == "male") {
    student->set_gender(Lab::Student::MALE);
  } else if (gender == "female") {
    student->set_gender(Lab::Student::FEMALE);
  } else {
    cerr << "no such type set default(male)" << endl;
    student->set_gender(Lab::Student::MALE);
  }
  
  cout << "Enter the student's phone" << endl;
  string phone;
  getline(cin, phone);
  student->set_phone(phone);
 
  cout << "Enter the student's email" << endl;
  string email;
  getline(cin, email);
  student->set_email(email);
  
}
 
int main(int argc, char * argv[]) {
  GOOGLE_PROTOBUF_VERIFY_VERSION;
  if (argc != 2) {
    cerr << "Usage:  " << argv[0] << " ADDRESS_BOOK_FILE" << endl;
    return -1;
  }
 
  // scope output
  {
    Lab::Student student;
    setStudent(&student);
    fstream output(argv[1], ios::out | ios::trunc | ios::binary);
    if (!student.SerializeToOstream(&output)) {
      cerr << "Failed to write address book." << endl;
      return -1;
    } else {
      string output;
      student.SerializeToString(&output);
      //cout << output << endl;
    }
  }
 
  // scope input
  {
    Lab::Student student;
    fstream input(argv[1], ios::in | ios::binary);
    if (!input) {
      cout << argv[1] << ": File not found.  Creating a new file." << endl;
    } else if (!student.ParseFromIstream(&input)) {
      cerr << "Failed to parse address book." << endl;
      return -1;
    } else {
      cout.setf(ios::left);
      cout.fill(' ');
      cout << "student's id: \t\t" << student.sid() << endl;
      cout << "student's name: \t" << student.name() << endl;
      cout << "student's age: \t\t" << student.age() << endl;
      cout << "student's gender: \t" << student.gender() << endl;
      cout << "student's phone: \t" << student.phone() << endl;
      cout << "student's email: \t" << student.email() << endl;
    }
  }
  
  google::protobuf::ShutdownProtobufLibrary();
 
  return 0;
}


<info.input

ye jiaqi

13331314

20

male

18819473230

423093883@qq.com


使用如下命令来进行编译,为了方便,定义一个makefile:

main:proto student.pb.h test.cpp
g++ test.cpp student.pb.cc student.pb.h -lprotobuf -o student -W -g
./student test.txt <info.input
proto:student.proto
protoc -I=./ --cpp_out=./ ./student.proto
clean:
rm -f *.out *.h.gch
rm -f student
rm -f *.pb.h
rm -f *.pb.cc
rm -f *.txt


运行结果如下

然后我们用vim 的二进制方式打开存储的文件,分析其中部分的结构

开始的前两位OAprotobuf存储文件时候预定义的两位,结束时候也是

 

字符串:“ye jiaqi” : 08则是 由tag = 1 的预定义的name protobuf枚举类型中的2得到, 1 << 3 | 2 = 08 H, 后面的是utf8编码的字符串内容,可以被vim识别

 

INT32 “13331314”: 按照我们之前的编码方式

2 >> 3 | 0 = 10H

13331314CD6B72H)使用variant方式编码得到的是F2D6AD06H, 跟我们的编码方式完全一致

 

总结

protobuf是一个很好的轻量级的编码方式,比XML要好。然而json也是一种轻量级的数据传输协议。查了一下资料,json在许多报文方面还是不太行,protobuf不为一种很好的选择。可是protobuf文件的可读性约等于0,这个缺点实在有些。。。。


© 著作权归作者所有

共有 人打赏支持
yejq8
粉丝 0
博文 11
码字总数 8967
作品 0
广州
程序员
Google Protocol Buffer(protoc, protobuf, pb)学习笔记

以前玩 C,Json、XML 什么的看多了,现在开始玩 C++,才发现我了解的世界太小了——原来 C++ 届还有 Google Protocol Buffers 这么好的东西。果然在 PC 上做开发真是好,不用考虑可执行程序的...

amc ⋅ 2017/07/07 ⋅ 0

Protocol Buffer的理解

Protocol Buffers是谷歌提供的一种用来序列化结构体数据的机制,类似于XML。官网上这么定义: Protocol buffers are a language-neutral, platform-neutral extensible mechanism for seria...

兔之 ⋅ 2015/06/22 ⋅ 0

Protocol Buffers在windwos下生成对应语言类文件

1:windows下开发,下载源码包和windows下的编译器 https://developers.google.com/protocol-buffers/docs/downloads 下载 Protocol Buffers 2.6.1 full source和 Protocol Compiler 2.6.1 ......

伊人梦醉 ⋅ 2015/12/29 ⋅ 0

Google Protocol Buffers 概述

个人小站,正在持续整理中,欢迎访问:http://shitouer.cn 小站博文地址:Google Protocol Buffers 概述 推荐阅读顺序,希望给你带来收获~ 《Google Protocol Buffers 概述》 《Google Protocol...

SibylY ⋅ 2016/04/14 ⋅ 0

Protocol Buffers Editor 95c 发布

Protocol Buffers Editor 95c 发布,该版本增加对 Protocol Buffers 扩展的支持。 Protocol Buffers Editor 是一个用来查看和编辑 Google 的 Protocol Buffers 的二进制文件的工具。 Protoc...

oschina ⋅ 2014/06/11 ⋅ 5

Some Notes of Protocol Buffer C++

Some Notes of Protocol Buffer C++ Operating System: Ubuntu 14.04 Language: C++ 1. Refer to the official guided documentations for the Installation of Protocol Buffer: Overview......

JiaMing ⋅ 2015/11/22 ⋅ 0

rpc协议之学习路线

RPC(Remote Procedure Call,远程过程调用)框架是分布式服务的基石,实现RPC框架需要考虑方方面面。其对业务隐藏了底层通信过程(TCP/UDP、打包/解包、序列化/反序列化),使上层专注于功能...

技术小胖子 ⋅ 2017/11/01 ⋅ 0

Protocol Buffers Editor 0.85 发布

Protocol Buffers Editor 0.85 发布,该版本对界面做了调整,更改了查找功能,包括 CSV 编辑器 和 XML 查看器。 Protocol Buffers Editor 是一个用来查看和编辑 Google 的 Protocol Buffers...

oschina ⋅ 2012/05/22 ⋅ 0

Protocol Buffers Editor

Protocol Buffers Editor 是一个用来查看和编辑 Google 的 Protocol Buffers 的二进制文件的工具。 Protocol Buffers是Google公司开发的一种数据描述语言,类似于XML能够将结构化数据序列化,...

匿名 ⋅ 2010/07/27 ⋅ 0

在 Android 应用程序中使用 Internet 数据(XML,JSON,Protocol Bu

Android 应用程序必须访问位于 Internet 上的数据,而 Internet 数据可以有几种不同的格式。本文将介绍在 Android 应用程序中如何使用三种数据格式: XML JSON Google 的 protocol buffers 首...

红薯 ⋅ 2010/08/08 ⋅ 4

没有更多内容

加载失败,请刷新页面

加载更多

下一页

知乎Java数据结构

作者:匿名用户 链接:https://www.zhihu.com/question/35947829/answer/66113038 来源:知乎 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 感觉知乎上嘲讽题主简...

颖伙虫 ⋅ 今天 ⋅ 0

Confluence 6 恢复一个站点有关使用站点导出为备份的说明

推荐使用生产备份策略。我们推荐你针对你的生产环境中使用的 Confluence 参考 Production Backup Strategy 页面中的内容进行备份和恢复(这个需要你备份你的数据库和 home 目录)。XML 导出备...

honeymose ⋅ 今天 ⋅ 0

JavaScript零基础入门——(九)JavaScript的函数

JavaScript零基础入门——(九)JavaScript的函数 欢迎回到我们的JavaScript零基础入门,上一节课我们了解了有关JS中数组的相关知识点,不知道大家有没有自己去敲一敲,消化一下?这一节课,...

JandenMa ⋅ 今天 ⋅ 0

火狐浏览器各版本下载及插件httprequest

各版本下载地址:http://ftp.mozilla.org/pub/mozilla.org//firefox/releases/ httprequest插件截至57版本可用

xiaoge2016 ⋅ 今天 ⋅ 0

Docker系列教程28-实战:使用Docker Compose运行ELK

原文:http://www.itmuch.com/docker/28-docker-compose-in-action-elk/,转载请说明出处。 ElasticSearch【存储】 Logtash【日志聚合器】 Kibana【界面】 答案: version: '2'services: ...

周立_ITMuch ⋅ 今天 ⋅ 0

使用快嘉sdkg极速搭建接口模拟系统

在具体项目研发过程中,一旦前后端双方约定好接口,前端和app同事就会希望后台同事可以尽快提供可供对接的接口方便调试,而对后台同事来说定好接口还仅是个开始、设计流程,实现业务逻辑,编...

fastjrun ⋅ 今天 ⋅ 0

PXE/KickStart 无人值守安装

导言 作为中小公司的运维,经常会遇到一些机械式的重复工作,例如:有时公司同时上线几十甚至上百台服务器,而且需要我们在短时间内完成系统安装。 常规的办法有什么? 光盘安装系统 ===> 一...

kangvcar ⋅ 昨天 ⋅ 0

使用Puppeteer撸一个爬虫

Puppeteer是什么 puppeteer是谷歌chrome团队官方开发的一个无界面(Headless)chrome工具。Chrome Headless将成为web应用自动化测试的行业标杆。所以我们很有必要来了解一下它。所谓的无头浏...

小草先森 ⋅ 昨天 ⋅ 0

Java Done Right

* 表示难度较大或理论性较强。 ** 表示难度更大或理论性更强。 【Java语言本身】 基础语法,面向对象,顺序编程,并发编程,网络编程,泛型,注解,lambda(Java8),module(Java9),var(...

风华神使 ⋅ 昨天 ⋅ 0

Linux系统日志

linux 系统日志 /var/log/messages /etc/logrotate.conf 日志切割配置文件 https://my.oschina.net/u/2000675/blog/908189 logrotate 使用详解 dmesg 命令 /var/log/dmesg 日志 last命令,调......

Linux学习笔记 ⋅ 昨天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部