Protocol Buffer使用
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 更新一个数据类型
在更新一个数据类型时更多的是需要考虑与旧版本的兼容性问题:
- 不要改变任何已存在字段的Tag值,如果改变Tag值可能会导致数值类型不匹配,具体原因参加protocol编码
- 建议使用
optional
和repeated
字段限制,尽可能的减少required
的使用. - 不需要的字段可以删除,删除字段的Tag不应该在新的消息定义中使用.
- 不需要的字段可以转换为扩展,反之亦然只要类型和数值依然保留
int32
,uint32
,int64
,uint64
, 和bool
是相互兼容的,这意味着可以将其中一种类型任意改编为另外一种类型而不会产生任何问题sint32
和sint64
是相互兼容的string
和bytes
是相互兼容的fixed32
兼容sfixed32
,fixed64
兼容sfixed64
.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编译器会对每一个字段生成一些get
和set
方法,这些方法的名称采用标识符所有小写加上相应的前缀或后缀组成.生成一个值为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.下载安装:
google protocol buffer 的官网地址是:http://code.google.com/p/protobuf/
建议下载稳定版本:protobuf-2.4.1 linux下载protobuf-2.4.1.tar.bz2 windows下载protobuf-2.4.1.zip
这里以linux下安装为实例:
tar -xvf protobuf-2.4.1.tar.bz2
cd protobuf-2.4.1
./configure --prefix=/usr/local/protobuf-2.4.1
make
make install
2.使用protobuf
查看编译生成的目录
cd /usr/local/protobuf-2.4.1
ls
bin include lib
其中,bin中的protoc是.proto文件的处理器,可用这个工具生成cpp,Java,Python文件.
由于系统常用这个工具,可以将其ln或者直接拷贝到系统环境bin下
ln -s /usr/local/protobuf-2.4.1/bin/protoc /usr/bin/protoc
同样,可以将头文件ln或者直接拷贝到系统环境
ln -s /usr/local/protobuf-2.4.1/include/google /usr/include/google
将lib文件ln或者直接拷贝到系统环境
略,方法同上.
这个时候,protobuf的开发环境已经搭建了.
3.如何使用protobuf
- 数据结构体:
- message message_name{message_body;}
- message_body格式:
- 例如required int32 query = 1[defaut=10];
- 形式为:rule type name = value[other_rule];
- 规则:
- required表示必须具有该值域;
- optional表示可选的值域;
- repeated表示可重复的值域(即>=0);
- 其中requered/optional是常用rule,而repeated则不常用同时因为是历史遗留现使用repeated int32 samples=4[packed=true];形式;
- value值:
- value值最小为1,是底层编码时使用其中1-15占一位,>15则会占多位;
- 不同的message中的value值互不干扰,常以1开始计数。
- 数据类型之基本类型:
- .proto Type C++ Type Java Type
- double double double
- float float float
- int32 int32 int
- int64 int64 long
- uint32 uint32 int
- uint64 uint64 long
- sint32 int32 int
- sint64 int64 long
- fixed32 uint32 int
- fixed64 uint64 long
- sfixed32 int32 int
- sfixed64 int64 long
- bool bool boolean
- string string String
- bytes string ByteString
- 数据类型之复杂类型:
- 复杂类型主要包括:枚举,其他message,groups等。
- 枚举定义例如:enum Corpus{WEB=0;LOCAL=1}
- 枚举定义在message中。
- 可以使用其他message作为类型来定义成员。
- groups我的理解有些像C++中的union结构。
- 嵌套定义:
- 可以嵌套定义message结构,而嵌套定义的message被其他message作为成员类型时需要形式为outmessage.inmessage形式。
- 包结构:
- 定义形式:package foo.bar;
- 对应C++中则生成两个命名空间foo和bar,且bar定义在foo中;
- 可以通过import "myproject/other_protos.proto";来引入.proto文件;
- 引用其他package中message时需要完整的package路径;
- Services:
- 主要用于RPC系统中,在.proto中定义接口;
- 定义形式如例子:
- service SearchService {
- rpc Search(SearchRequest) return (SearchResponse);
- }
- .proto文件编译:
- 格式:
- protoc -–proto_path=(.proto文件路径) -–cpp_out=(.cc .java生成文件路径) (.proto文件路径)/?.proto
- -–proto_path 简化为: --I
- 其中可根据需要更改:cpp_out选项为java_out/python_out。
- 例子:
- protoc -I=./ --cpp_out=./ model.proto
我们拿个例子:
建立model.proto
- package cn.vicky.model.seri;
- message User {
- required int32 id = 1; // 主键,唯一
- required string username = 2; // 帐号
- required string password = 3; // 密码
- optional string email = 4; // 邮箱(可选)
- repeated Person person = 5; // 账户拥有的角色(可以重复)
- }
- message Person {
- required int32 id = 1; // 主键,唯一
- required string name = 2; // 角色名字
- repeated PhoneNumber phone = 3; // 电话号码(可以重复)
- }
- // 枚举类型
- enum PhoneType {
- MOBILE = 0;
- HOME = 1;
- WORK = 2;
- }
- message PhoneNumber {
- required string number = 1;
- optional PhoneType type = 2 [default = HOME];
- }
protoc -I=./ --cpp_out=./ model.proto
将生成对应的model.pb.h model.pb.cc
使用:
编写main.cpp
- /*
- * File: main.cpp
- * Author: Vicky.H
- * Email: eclipser@163.com
- */
- #include <iostream>
- #include <fstream>
- #include "model.pb.h"
- /*
- *
- */
- int main(void) {
- // 创建User对象
- cn::vicky::model::seri::User u;
- u.set_id(1);
- u.set_username("Jack");
- u.set_password("123456");
- u.set_email("289997171@qq.com");
- // 创建User中的一个角色
- cn::vicky::model::seri::Person* _person1 = u.add_person();
- _person1->set_id(1);
- _person1->set_name("P1");
- // 创建角色中的一个电话号码:1
- cn::vicky::model::seri::PhoneNumber* _phone1 = _person1->add_phone();
- _phone1->set_number("+8613618074943");
- _phone1->set_type(cn::vicky::model::seri::MOBILE);
- // 创建角色中的一个电话号码:2
- cn::vicky::model::seri::PhoneNumber* _phone2 = _person1->add_phone();
- _phone2->set_number("02882334717");
- _phone2->set_type(cn::vicky::model::seri::WORK);
- // 创建User中的一个角色
- cn::vicky::model::seri::Person* _person2 = u.add_person();
- _person2->set_id(2);
- _person2->set_name("P2");
- // 创建角色中的一个电话号码:1
- cn::vicky::model::seri::PhoneNumber* _phone3 = _person2->add_phone();
- _phone3->set_number("+8613996398667");
- _phone3->set_type(cn::vicky::model::seri::MOBILE);
- // 创建角色中的一个电话号码:2
- cn::vicky::model::seri::PhoneNumber* _phone4 = _person2->add_phone();
- _phone4->set_number("02882334717");
- _phone4->set_type(cn::vicky::model::seri::WORK);
- // 持久化:
- // std::fstream out("User.pb", std::ios::out | std::ios::binary | std::ios::trunc);
- // u.SerializeToOstream(&out);
- // out.close();
- // 对象化:
- cn::vicky::model::seri::User u2;
- std::fstream in("User.pb", std::ios::in | std::ios::binary);
- if (!u2.ParseFromIstream(&in)) {
- std::cerr << "Failed to parse User.pb." << std::endl;
- exit(1);
- }
- std::cout << u2.id() << std::endl;
- std::cout << u2.username() << std::endl;
- std::cout << u2.password() << std::endl;
- std::cout << u2.email() << std::endl;
- std::cout << "---------------------------" << std::endl;
- for(int i = 0;i < u2.person_size();i++) {
- cn::vicky::model::seri::Person* p = u2.mutable_person(i);
- std::cout << p->id() << std::endl;
- std::cout << p->name() << std::endl;
- for (int j = 0;j < p->phone_size();j++) {
- cn::vicky::model::seri::PhoneNumber* phone = p->mutable_phone(j);
- std::cout << phone->number() << std::endl;
- }
- std::cout << "---------------------------" << std::endl;
- }
- return 0;
- }
需要 -lpthread -lprotobuf (protobuf已经被加载到了/usr/lib)
执行后,会生成:User.pb,存储的二进制文件.可以直接打开看看.
以上,我们使用了protobuf完成c++下的对象序列化以及反序列化.这里我们要描述一下protobuf的优势了.
那就是protobuf性能高效,他的序列化速度比java自身的序列化还快数倍,而且支持3种语言对象的转换.以往,在C++中序列化的对象,比
如用boost
serialization持久化的对象,无法用java展开,即便使用jni技术,这也是非常麻烦的事.现在我们有protobuf了.
运行: protoc -I=./ --java_out=./ model.proto 将生成对应的Java类
我们可以用Maven建立一个Java工程.需要protobuf的java依赖库:
- <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
- <modelVersion>4.0.0</modelVersion>
- <groupId>cn.vicky</groupId>
- <artifactId>google_protobuf_01_java</artifactId>
- <version>1.0-SNAPSHOT</version>
- <packaging>jar</packaging>
- <name>google_protobuf_01_java</name>
- <url>http://maven.apache.org</url>
- <properties>
- <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
- </properties>
- <dependencies>
- <dependency>
- <groupId>com.google.protobuf</groupId>
- <artifactId>protobuf-java</artifactId>
- <version>2.4.1</version>
- </dependency>
- </dependencies>
- </project>
编写Test.java
- package cn.vicky.model.seri;
- import java.io.File;
- import java.io.FileInputStream;
- import java.io.FileNotFoundException;
- import java.io.IOException;
- import java.io.InputStream;
- /**
- *
- * @author Vicky.H
- */
- public class Test {
- public static void main(String args[]) throws FileNotFoundException, IOException {
- File file = new File("User.pb");
- InputStream is = new FileInputStream(file);
- Model.User user = Model.User.parseFrom(is);
- System.out.println(user.getId());
- System.out.println(user.getUsername());
- System.out.println(user.getPassword());
- System.out.println(user.getEmail());
- System.out.println("-------------------");
- for (Model.Person person : user.getPersonList()) {
- System.out.println(person.getId());
- System.out.println(person.getName());
- for (Model.PhoneNumber phone : person.getPhoneList()) {
- System.out.println(phone.getNumber());
- }
- System.out.println("-------------------");
- }
- is.close();
- }
- }
运行:
1
Jack
123456
289997171@qq.com
---------------------------
1
P1
+8613618074943
02882334717
---------------------------
2
P2
+8613996398667
02882334717
---------------------------
运行 SUCCESSFUL (总时间: 594ms)
OK.以上我们完成了probuf在C++,Java的使用.非常强力是不是!!
设计思想:
在POJO中,protobuf生成的类,处于PO状态,而且这个生成的类,我们最好不要做任何修改或太大的修改,那么,这个时候,我们可以通过C++友元类的方式,为PO添加一个JO类.将数据结构算法分离,也就是说,PO是数据,JO放算法!!!
与数据库的结合:
MySQL oracle 可以很轻松的存储,读取二进制.还有一点,那就是通过这种方式,我们可以非常简单的将C++的对象,持久化的redis之类内存数据库了.
附:
model.proto也可以这样定义,不过,本人认为,上面的更好,这里仅供参考,采用什么样的方式,生成的类的结构也不太一样.
- package cn.vicky.model.seri;
- message User {
- required int32 id = 1; // 主键,唯一
- required string username = 2; // 帐号
- required string password = 3; // 密码
- optional string email = 4; // 邮箱(可选)
- message Person {
- required int32 id = 1; // 主键,唯一
- required string name = 2; // 角色名字
- // 枚举类型
- enum PhoneType {
- MOBILE = 0;
- HOME = 1;
- WORK = 2;
- }
- message PhoneNumber {
- required string number = 1;
- optional PhoneType type = 2 [default = HOME];
- }
- repeated PhoneNumber phone = 3; // 电话号码(可以重复)
- }
- repeated Person person = 5; // 账户拥有的角色(可以重复)
- }
Protocol Buffer使用的更多相关文章
- Protocol Buffer搭建及示例
本文来源:http://www.tanhao.me/code/150911.html/ Protocol Buffer(简称Protobuf或PB)是由Google推出的一种数据交换格式,与传统的XM ...
- 从零开始山寨Caffe·伍:Protocol Buffer简易指南
你为Class外访问private对象而苦恼嘛?你为设计序列化格式而头疼嘛? ——欢迎体验Google Protocol Buffer 面向对象之封装性 历史遗留问题 面向对象中最矛盾的一个特性,就是 ...
- [原创翻译]Protocol Buffer Basics: C#
Protocol Buffer 基础知识:c# 原文地址:https://developers.google.com/protocol-buffers/docs/csharptutorial ...
- Google Protocol Buffer 的使用和原理[转]
本文转自: http://www.ibm.com/developerworks/cn/linux/l-cn-gpb/ Protocol Buffers 是一种轻便高效的结构化数据存储格式,可以用于结构 ...
- Google Protocol Buffer 的使用
简介 Google Protocol Buffer( 简称 Protobuf) 是 Google 公司内部的混合语言数据标准,目前已经正在使用的有超过 48,162 种报文格式定义和超过 12,183 ...
- 学习Google Protocol buffer之语法
上一篇结尾的时候问了几个问题,其实主要就是这个protoBuffer协议的语法,弄清楚语法后边才好开展工作嘛,不然大眼而对小眼儿,互相不认识,就没法玩耍了.其实就是学习怎么用google提供的这套 p ...
- 学习Google Protocol buffer之概述
XML这种属于非常强大的一种格式,能存储任何你想存的数据,而且编辑起来还是比较方便的.致命的缺陷在于比较庞大,在某些情况下,序列化和解析都会成为瓶颈.这种对于实时性很强的应用来说,就不太适合了,想象下 ...
- Google Protocol Buffer 简单介绍
以下内容主要整理自官方文档. 为什么使用 Protocol Buffers .proto文件 Protocol Buffers 语法 编译.proto文件 Protocol Buffers API 枚 ...
- Ggoogle Protocol Buffer的使用 (基于C++语言)
首先说明的是Protocol Buffle是灵活高效的.它的一个很好的优点(很重要的,我认为)就是后向兼容性--当我们扩展了了.proto文件后,我们照样可以用它来读取之前生成的文件. 之前已经写了关 ...
- Google Protocol Buffer的安装与.proto文件的定义
什么是protocol Buffer呢? Google Protocol Buffer( 简称 Protobuf) 是 Google 公司内部的混合语言数据标准. 我理解的就是:它是一种轻便高效的结构 ...
随机推荐
- oracle PL/SQL(procedure language/SQL)程序设计
PL/SQL(procedure language/SQL)语言是Oracle对SQL语言的过程化扩充,是一个完整的编程语言.PL/SQL实现了过程化语句(如分支.循环等)与SQL语句的无缝连接,将过 ...
- 剑指Offer27 数组中超过一半的数
/************************************************************************* > File Name: 27_MoreTh ...
- Linux apache日志分析常用命令汇总
1.查看当天有多少个IP访问: awk '{print $1}' log_file|sort|uniq|wc –l 2.查看某一个页面被访问的次数: grep "/index.php&quo ...
- 关于CSS的只言片语
这段时间做了一个简单的页面,借机又重温了一下CSS的相关知识,现总结一下: 工欲善其事必先利其器,让我们先做一点准备工作 1.在页面添加: <meta http-equiv="x-ua ...
- Javascript delete 引用类型对象
很少使用javascript的delete,最近因为一个小bug发现删除引用类型对象的时候有一点不同.如下面例子: var testVar = { a : { test : 1 } }, test1 ...
- python中列表 元组 字符串如何互相转换
python中有三个内建函数:列表,元组和字符串,他们之间的互相转换使用三个函数,str(),tuple()和list(),具体示例如下所示: >>> s = "xxxxx ...
- 【SNMP】SNMP概述
SNMP概述 SNMP(Simple Network Management Protocol,简单网络管理协议)是目前UDP/IP网络中应用最为广泛的网络管理协议,它提供了一个管理框架来监控和维护互联 ...
- C# 中 datagridview 绑定BindingList类型和更新
C# 中的datagridview是一个非常有用且强大的控件,可以用来绑定数据库.绑定LIST类型的变量等等. 这里我们说一说绑定List类型并实时更新datagridview的情况.实时更新,指的是 ...
- 8款功能强大的最新HTML5特效实例
1.HTML5 Canvas画板画图工具 可定义笔刷和画布 今天要分享的这款HTML5 Canvas画图工具就可以简单实现网络画图的功能,我们可以自定义笔刷的类型.粗细.颜色,也可以定义画布的大小和背 ...
- ArcGIS Desktop开发基础(转)
http://www.cnblogs.com/maweifeng/archive/2006/07/19/455024.html 原文地址 ArcGIS Desktop开发的类型 ○ 自定义ArcMa ...