Protocol Buffer使用简介

我们项目中使用protocol buffer来进行服务器和客户端的消息交互,服务器使用C++,所以本文主要描述protocol buffer C++方面的使用,其他语言方面的使用参见google的官方文档.

1.概览

1.1 什么是protocol buffer

protocol buffer是google的一个开源项目,它是用于结构化数据串行化的灵活、高效、自动的方法,例如XML,不过它比xml更小、更快、也更简单。你可以定义自己的数据结构,然后使用代码生成器生成的代码来读写这个数据结构。你甚至可以在无需重新部署程序的情况下更新数据结构。

2.使用

2.1定义一个消息类型

message SearchRequest
{
required string query = 1;
optional int32 page_number = 2;// Which page number do we want?
optional int32 result_per_page = 3;// Number of results to return per page.
}

该消息定义了三个字段,两个int32类型和一个string类型的字段,每个字段由字段限制,字段类型,字段名和Tag四部分组成.对于C++,每一个.proto文件经过编译之后都会对应的生成一个.h和一个.cc文件.

字段限制

字段限制共有3类: required:必须赋值的字段 optional:可有可无的字段 repeated:可重复字段(变长字段),类似于数值 由于一些历史原因,repeated字段并没有想象中那么高效,新版本中允许使用特殊的选项来获得更高效的编码:

repeated int32 samples = 4 [packed=true];

Tags

消息中的每一个字段都有一个独一无二的数值类型的Tag.1到15使用一个字节编码,16到2047使用2个字节编码,所以应该将Tags 1到15留给频繁使用的字段. 可以指定的最小的Tag为1, 最大为2^{29}-1或536,870,911.但是不能使用19000到19999之间的值,这些值是预留给protocol buffer的.

注释

使用C/C++的//语法来添加字段注释.

2.2 值类型

proto的值类型与具体语言中值类型的对应关系.

2.3 可选字段与缺省值

在消息解析时,如果发现消息中没有包含可选字段,此时会将消息解析对象中相对应的字段设置为默认值,可以通过下面的语法为optional字段设置默认值:

optional int32 result_per_page = 3 [default = 10];

如果没有指定默认值,则会使用系统默认值,对于string默认值为空字符串,对于bool默认值为false,对于数值类型默认值为0,对于enum默认值为定义中的第一个元素.

2.4 枚举

message SearchRequest
{
required string query = 1;
optional int32 page_number = 2;
optional int32 result_per_page = 3 [default = 10];
enum Corpus
{
UNIVERSAL = 0;
WEB = 1;
IMAGES = 2;
LOCAL = 3;
NEWS = 4;
PRODUCTS = 5;
VIDEO = 6;
}
optional Corpus corpus = 4 [default = UNIVERSAL];
}

由于枚举值采用varint编码,所以为了提高效率,不建议枚举值取负数.这些枚举值可以在其他消息定义中重复使用.

2.5 使用其他消息类型

可以使用一个消息的定义作为另一个消息的字段类型.

message Result
{
required string url = 1;
optional string title = 2;
repeated string snippets = 3;
} message SearchResponse
{
repeated Result result = 1;
}

可以使用import语法来包含另外一个.proto文件.

import "myproject/other_protos.proto";

2.6 嵌套类型

在protocol中可以定义如下的嵌套类型

message SearchResponse
{
message Result
{
required string url = 1;
optional string title = 2;
repeated string snippets = 3;
}
repeated Result result = 1;
}

如果在另外一个消息中需要使用Result定义,则可以通过Parent.Type来使用.

message SomeOtherMessage
{
optional SearchResponse.Result result = 1;
}

protocol支持更深层次的嵌套和分组嵌套,但是为了结构清晰起见,不建议使用过深层次的嵌套,建议通过 2.5 小节提到的方法来实现.

2.7 更新一个数据类型

在更新一个数据类型时更多的是需要考虑与旧版本的兼容性问题:

  1. 不要改变任何已存在字段的Tag值,如果改变Tag值可能会导致数值类型不匹配,具体原因参加protocol编码
  2. 建议使用optionalrepeated字段限制,尽可能的减少required的使用.
  3. 不需要的字段可以删除,删除字段的Tag不应该在新的消息定义中使用.
  4. 不需要的字段可以转换为扩展,反之亦然只要类型和数值依然保留
  5. int32, uint32, int64, uint64, 和bool是相互兼容的,这意味着可以将其中一种类型任意改编为另外一种类型而不会产生任何问题
  6. sint32sint64是相互兼容的
  7. stringbytes是相互兼容的
  8. fixed32 兼容 sfixed32, fixed64 兼容 sfixed64.
  9. optional 兼容repeated

2.8 扩展

extend特性来让你声明一些Tags值来供第三方扩展使用.

message Foo
{
// ...
extensions 100 to 199;
}

假如你在你的proto文件中定义了上述消息,之后别人在他的.proto文件中import你的.proto文件,就可以使用你指定的Tag范围的值.

extend Foo
{
optional int32 bar = 126;
}

在访问extend中定义的字段和,使用的接口和一般定义的有点不一样,例如set方法:

    Foo foo;
foo.SetExtension(bar, 15);

类似的有HasExtension(), ClearExtension(), GetExtension(), MutableExtension(), and AddExtension()等接口.

2.9 选项

  • optimize_for (file option): 可以设置的值有SPEED, CODE_SIZE, 或 LITE_RUNTIME. 不同的选项会以下述方式影响C++, Java代码的生成.T

    • SPEED (default): protocol buffer编译器将会生成序列化,语法分析和其他高效操作消息类型的方式.这也是最高的优化选项.确定是生成的代码比较大.
    • CODE_SIZE: protocol buffer编译器将会生成最小的类,确定是比SPEED运行要慢
    • LITE_RUNTIME: protocol buffer编译器将会生成只依赖"lite" runtime library (libprotobuf-lite instead of libprotobuf)的类. lite运行时库比整个库更小但是删除了例如descriptors 和 reflection等特性. 这个选项通常用于手机平台的优化.
option optimize_for = CODE_SIZE;

3.常用API介绍

对于如下消息定义:

// test.proto
message PBStudent
{
optional uint32 StudentID = 1;
optional string Name = 2;
optional uint32 Score = 3;
} message PBMathScore
{
optional uint32 ClassID = 1;
repeated PBStudent ScoreInf = 2;
}

protocol buffer编译器会为每个消息生成一个类,每个类包含基本函数,消息实现,嵌套类型,访问器等部分.

3.1 基本函数

public:
PBStudent();
virtual ~PBStudent(); PBStudent(const PBStudent& from); inline PBStudent& operator=(const PBStudent& from) {
CopyFrom(from);
return *this;
} inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const {
return _unknown_fields_;
} inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() {
return &_unknown_fields_;
} static const ::google::protobuf::Descriptor* descriptor();
static const PBStudent& default_instance(); void Swap(PBStudent* other);

3.2 消息实现

PBStudent* New() const;
void CopyFrom(const ::google::protobuf::Message& from);
void MergeFrom(const ::google::protobuf::Message& from);
void CopyFrom(const PBStudent& from);
void MergeFrom(const PBStudent& from);
void Clear();
bool IsInitialized() const; int ByteSize() const;
bool MergePartialFromCodedStream(
::google::protobuf::io::CodedInputStream* input);
void SerializeWithCachedSizes(
::google::protobuf::io::CodedOutputStream* output) const;
::google::protobuf::uint8* SerializeWithCachedSizesToArray(::google::protobuf::uint8* output) const;
int GetCachedSize() const { return _cached_size_; }
private:
void SharedCtor();
void SharedDtor();
void SetCachedSize(int size) const;

3.3 嵌套类型

3.4 访问器

// optional uint32 StudentID = 1;
inline bool has_studentid() const;
inline void clear_studentid();
static const int kStudentIDFieldNumber = 1;
inline ::google::protobuf::uint32 studentid() const;
inline void set_studentid(::google::protobuf::uint32 value); // optional string Name = 2;
inline bool has_name() const;
inline void clear_name();
static const int kNameFieldNumber = 2;
inline const ::std::string& name() const;
inline void set_name(const ::std::string& value);
inline void set_name(const char* value);
inline void set_name(const char* value, size_t size);
inline ::std::string* mutable_name();
inline ::std::string* release_name();
inline void set_allocated_name(::std::string* name); // optional uint32 Score = 3;
inline bool has_score() const;
inline void clear_score();
static const int kScoreFieldNumber = 3;
inline ::google::protobuf::uint32 score() const;
inline void set_score(::google::protobuf::uint32 value);

protocol buffer编译器会对每一个字段生成一些getset方法,这些方法的名称采用标识符所有小写加上相应的前缀或后缀组成.生成一个值为Tags的k标识符FieldNum常量,

3.5 其他函数

除了生成上述类型的方法外, 编译器还会生成一些用于消息类型处理的私有方法. 每一个.proto文件在编译的时候都会自动包含message.h文件,这个文件声明了很多序列化和反序列化,调试, 复制合并等相关的方法.

3.6 使用例子

在我们平时的使用中,通常一个message对应一个类,在对应的类中定义一个set和create方法来生成和解析PB信息.针对上述消息定义如下类:

// test.h
class CStudent
{
public:
unsigned mStudentID;
unsigned mScore;
string mName; CStudent()
{
Init();
} inline void Init()
{
mStudentID = 0;
mScore = 0;
mName = "";
}
} class CMathScore
{
private:
unsigned mClassID;
CStudent mScoreInf[100];
public:
CMathSCore()
{
Init();
}
~CMathScore() {}; void Init();
void SetFromPB(const PBMathScore* pPB);
void CreatePB(PBMathScore* pPB); // Get & Set mClassID
...
// Get & set mScoreInf
...
// some other function
...
}

对应的cpp文件中实现对PB的操作

// test.cpp
void CMathScore::Init()
{
mClassID = 0;
memset(mScoreInf, 0, sizeof(mScoreInf));
} void CMathScore::SetFromPB(const PBMathScore* pPB)
{
if ( NULL == pPB ) return; mClassID = pPB->classid();
for(unsigned i = 0; i < (unsigned)pPB->scoreinf_size() && i < 100; ++i)
{
PBStudent* pStu = pPB->mutable_scoreinf(i);
mScoreInf[i].mStudentID = pStu->studentid();
mScoreInf[i].mScore = pStu->score();
mScoreInf[i].mName = pStu->name();
}
} void CMathScore::CreatePB(PBMathScore* pPB)
{
if ( NULL == pPB ) return; pPB->set_classid(mClassID);
for(unsigned i = 0; i < 100; ++i)
{
PBStudent* pStu = pPB->add_scoreinf();
pStu->set_studentid(mScoreInf[i].mStudentID)
pStu->set_score(mScoreInf[i].mScore);
pStu->set_name(mScoreInf[i].mName);
}
}

PB文件的读写

// use.cpp
#include<test.h> #defind MAX_BUFFER 1024 * 1024
int write()
{
CMathScore mMath;
PBMathScore mPBMath;
// use set functions to init member variable fstream fstm("./math.dat", ios::out | ios::binary);
if ( fstm.is_open() == false )
{
return -1;
}
char* tpBuffer = (char*)malloc(MAX_BUFFER);
if ( NULL == tpBuffer )
{
return -2;
} mMath.CreatePB(&mPBMath);
if ( mPBMath.SerializeToArray(tpBuffer, mPBMath.ByteSize()) == false )
{
return -3;
}
fstm.write(tpBuffer, mPBMath.ByteSize());
free(tpBuffer);
fstm.close(); return 0;
} int read()
{
CMathScore mMath;
PBMathScore mPBMath; fstream fstm.open("./math.dat", ios::out | ios::binary);
if ( fstm.is_open() == false )
{
return -1;
}
char* tpBuffer = (char*)malloc(MAX_BUFFER);
if ( NULL == tpBuffer )
{
return -2;
}
char* tpIdx = tpBuffer;
int tLen;
while ( !fstm.eof() && tLen < MAX_BUFFER )
{
fstm.read(tpIdx, 1);
tpIdx += 1;
tLen++;
}
if ( mPBMath.ParseFromArray(tpBuffer, tLen - 1) == false )
{
return -3;
}
fstm.close();
free(tpBuffer);
tpIdx = NULL; mMath.SetFromPB(&mPBMath);
// do some thing return 0;
}

自己编了一个股票监控软件,有如下功能,有兴趣的朋友可以下载;

(1)   个股监测。监测个股实时变化,可以监测个股大单交易、急速拉升和下降、主力入场和出场、股票最高点和最低点提醒。检测到最高点、最低点、主力进场点、主力退场点、急速拉升点、急速下跌点,给出语音或者声音提醒,不用再时刻看着大盘了,给你更多自由的时间;

(2)   大盘监测。监测大盘的走势,采用上证、深证、创业三大指数的综合指数作为大盘走势。并实时监测大盘的最高点和最低点、中间的转折点。

(3)   股票推荐。还能根据历史数据长期或短期走势进行分析,对股市3千多个股票进行分析对比,选出涨势良好的股票,按照增长速度从大到小排序,推荐给你涨势良好的股票;

下载地址:

1.0.3版本(修复大盘指数崩溃缺陷)下载地址:

链接:https://pan.baidu.com/s/1BJcTp-kdniM7VE9K5Kd3vg 提取码:003h

更新链接:

https://www.cnblogs.com/bclshuai/p/10621613.html

 

Google protocol buff使用的更多相关文章

  1. Google Protocol Buffer 的使用和原理[转]

    本文转自: http://www.ibm.com/developerworks/cn/linux/l-cn-gpb/ Protocol Buffers 是一种轻便高效的结构化数据存储格式,可以用于结构 ...

  2. Google Protocol Buffer 的使用

    简介 Google Protocol Buffer( 简称 Protobuf) 是 Google 公司内部的混合语言数据标准,目前已经正在使用的有超过 48,162 种报文格式定义和超过 12,183 ...

  3. 学习Google Protocol buffer之概述

    XML这种属于非常强大的一种格式,能存储任何你想存的数据,而且编辑起来还是比较方便的.致命的缺陷在于比较庞大,在某些情况下,序列化和解析都会成为瓶颈.这种对于实时性很强的应用来说,就不太适合了,想象下 ...

  4. Google Protocol Buffer的安装与.proto文件的定义

    什么是protocol Buffer呢? Google Protocol Buffer( 简称 Protobuf) 是 Google 公司内部的混合语言数据标准. 我理解的就是:它是一种轻便高效的结构 ...

  5. Google Protocol Buffer 的使用和原理

    Google Protocol Buffer 的使用和原理 Protocol Buffers 是一种轻便高效的结构化数据存储格式,可以用于结构化数据串行化,很适合做数据存储或 RPC 数据交换格式.它 ...

  6. Google Protocol Buffer 的编码方式

    Google Protocol Buffer 使用到了两种编码方式:Varints 和 zigzag. 一 Varints 编码 每个 byte 只用 7bit 表示数字,最高位 bit作为标志位,如 ...

  7. Google Protocol Buffer 协议

    1. Protocol Buffers 简介 Protocol Buffers (ProtocolBuffer/ protobuf )是Google公司开发的一种数据描述语言,类似于XML能够将结构化 ...

  8. Google Protocol Buffer

    Google Protocol Buffer(protobuf)是一种高效且格式可扩展的编码结构化数据的方法.和JSON不同,protobuf支持混合二进制数据,它还有先进的和可扩展的模式支持.pro ...

  9. 【Google Protocol Buffer】Google Protocol Buffer

    http://www.ibm.com/developerworks/cn/linux/l-cn-gpb/ Google Protocol Buffer 的使用和原理 Protocol Buffers ...

随机推荐

  1. python numpy 的用法—— bincount

    今天看脚本的时候遇到了几个不懂的用法,记录下来供日后查看: 1.numpy bincount 先上图: 如上所示:首先要求输入的数组不能包含负数: 该函数是计算非负元素的个数,如果数组中的最大值为10 ...

  2. vue源码分析

    1.new Vue的过程 1)首先Vue是一个类,初始化时已经添加很多 initMixin(Vue)             给原型添加Vue.prototype._init  stateMixin( ...

  3. 原创博客>>>解决粘包问题的方法

    目录 原创博客>>>解决粘包问题的方法 原创博客>>>解决粘包问题的方法 服务端: import socket import struct service=sock ...

  4. Oracle【子查询】

    Oracle子查询:当一个查询依赖于另外一个查询的结果的时候,就需要使用子查询.单行子查询 :筛选条件不明确,需要执行一次查询且查询结果只有一个字段且字段值只有一个.注意:where子句中允许出现查询 ...

  5. BLE 5协议栈-链路层

    文章转载自:http://www.sunyouqun.com/2017/04/page/3/ 链路层LL(Link Layer)是协议栈中最重要的一层. 链路层的核心是状态机,包含广播.扫描.发起和连 ...

  6. API开发之接口安全(一)----生成sign

    在对于API的开发中 最让人头疼的 就是接口数据暴露 让一些有心之人 抓包之后恶意请求 那么如何解决这一弊端呢?自然而然的 我们就想到了 加密  那我们又如何加密 如何解密 才能使之有最安全的效率呢? ...

  7. Python中关于csv的简单操作

    Python中关于csv的简单操作 CSV操作简单,直接import csv即可, 主要使用reader和pandas 1 reader的简单使用 csv.reader("1.csv&quo ...

  8. 浅入深出Vue:文章编辑

    登录与注册功能都已经实现,现在是时候来开发文章编辑功能了. 这里咱们就使用 markdown 作为编辑语言吧,简洁通用.那么我们就需要找一下 markdown 的编辑器组件了,而且还要支持 vue噢. ...

  9. html2canvas-html图片合成-canvas生成图片

    作用 html2canvas可以通过纯JS对浏览器端经行截屏,但截图的精确度还有待提高,部分css不可识别,所以在canvas中不能完美呈现原画面样式 支持的浏览器 Firefox 3.5+ Goog ...

  10. mysqldump 使用--tab=path参数时提示mysqldump: Got error: 1290: The MySQL server is running with the --secure-file-priv option so it cannot execute this statement when executing 'SELECT INTO OUTFILE'

    报错: [root@zedu test]# mysqldump -h127.0.0.1 -uroot -p --single-transaction --add-drop-database --tab ...