您的位置:首页 > Web前端

google protocol buffer (C++,Java序列化使用实例)

2015-08-04 10:07 423 查看
转载: http://blog.csdn.net/eclipser1987/article/details/8525383 (eclipser@163.com)

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

[cpp] view
plaincopy

数据结构体:

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

[cpp] view
plaincopy

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

[cpp] view
plaincopy

/*

* 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依赖库:

[html] view
plaincopy

<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

[java] view
plaincopy

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也可以这样定义,不过,本人认为,上面的更好,这里仅供参考,采用什么样的方式,生成的类的结构也不太一样.

[cpp] view
plaincopy

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; // 账户拥有的角色(可以重复)

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: