添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
首先来一发pb3的IDL代码Helloworld.proto
syntax = "proto2";

package proto.helloworld;

message HelloWorld
required int32 id = 1; // id
required string str = 2; // str
optional int32 opt = 3; // optional field
}

使用命令行或者编写一个bat批处理文件
@set path=..\..\..\..\third_party\protobuf\bin\release;%path%
:: //////////////////////////////////////////////////////////// /
::
:: 编译Protobuf协议
:: //////////////////////////////////////////////////////////// /
protoc --version

protoc --cpp_out=../ ./Helloworld.proto

pause
执行以上的批处理文件,将会在其目录的上一层目录下生成两个源码文件:Helloworld.pb.h和Helloworld.pb.cc.
将生成的这俩文件拖进工程里面去,至于vc的StdAfx,预编译头的问题,很好解决.在cc文件上点右键->属性->C/C++->预编译头->预编译头(不使用预编译头),这事儿就妥妥的解决了.

以下列举序列化到三种目标里去的实例
1.std::string
void testString()
//////////////////////////////////////////////////////////////////////// //
// 编码
//////////////////////////////////////////////////////////////////////// //
proto::helloworld::HelloWorld msg_encode;
msg_encode.set_id(10086);
msg_encode.set_str("hello world");
msg_encode.set_opt(10000);

std:: string str_data;
bool encode_ok = msg_encode.SerializeToString(&str_data);
ASSERT(encode_ok);

//////////////////////////////////////////////////////////////////////// //
// 解码
//////////////////////////////////////////////////////////////////////// //
proto::helloworld::HelloWorld msg_decode;
bool decode_ok = msg_decode.ParseFromString(str_data);
ASSERT(decode_ok);
size_t n = str_data.length();

ASSERT(msg_decode.id() == 10086);
ASSERT(msg_decode.str().compare("hello world") == 0);
ASSERT(msg_decode.opt() == 10000 );

2.ByteArray(char [])
void testByteArray()
//////////////////////////////////////////////////////////////////////// //
// 编码
//////////////////////////////////////////////////////////////////////// //
proto::helloworld::HelloWorld msg_encode;
msg_encode.set_id(10086);
msg_encode.set_str("hello world");
msg_encode.set_opt(10000);

char msg_buf[1024];
ZeroMemory(msg_buf, sizeof (msg_buf));
bool encode_ok = msg_encode.SerializeToArray(msg_buf, sizeof (msg_buf));
ASSERT(encode_ok);
int encode_size = msg_encode.ByteSize();

//////////////////////////////////////////////////////////////////////// //
// 解码
//////////////////////////////////////////////////////////////////////// //
proto::helloworld::HelloWorld msg_decode;
bool decode_ok = msg_decode.ParseFromArray(msg_buf, encode_size);
ASSERT(decode_ok);

ASSERT(msg_decode.id() == 10086);
ASSERT(msg_decode.str().compare("hello world") == 0);
ASSERT(msg_decode.opt() == 10000);

3.std::fstream
void testStream()
//////////////////////////////////////////////////////////////////////// //
// 编码
//////////////////////////////////////////////////////////////////////// //
proto::helloworld::HelloWorld msg_encode;
msg_encode.set_id(10086);
msg_encode.set_str("hello world");
msg_encode.set_opt(10000);

std::fstream output("./msg_bin", std::ios:: out | std::ios::trunc | std::ios::binary);
bool encode_ok = msg_encode.SerializeToOstream(&output);
ASSERT(encode_ok);
output.close();

//////////////////////////////////////////////////////////////////////// //
// 解码
//////////////////////////////////////////////////////////////////////// //
std::fstream input("./msg_bin", std::ios:: in | std::ios::binary);
proto::helloworld::HelloWorld msg_decode;
bool decode_ok = msg_decode.ParseFromIstream(&input);
ASSERT(decode_ok);

ASSERT(msg_decode.id() == 10086);
ASSERT(msg_decode.str().compare("hello world") == 0);
ASSERT(msg_decode.opt() == 10000);
以上就是ProtoBuf的基本用法了.


FlatBuffers

FlatBuffers支持将ProtoBuf的IDL转换为自己的IDL,只需要简单的一行命令:
flatc.exe --proto Helloworld.proto
该命令将会生成一个FlatBuffers的IDL文件:Helloworld.fbs.

Helloworld.fbs它看起来像是这样的:
// Generated from Helloworld.proto

namespace fbs.helloworld;

table HelloWorld {
id: int = 0 (id: 0);
str: string (required, id: 1);
opt: int = 0 (id: 2);
}

// 定义之后将会提供GetHelloWorld,VerifyHelloWorldBuffer,FinishHelloWorldBuffer三个方法.
root_type HelloWorld;
这是我基于生成的IDL修改过的.

我们可以用下面这个bat去生成代码:
flatc.exe --cpp -o ../ Helloworld.fbs
pause
执行之后,它将在当前目录的上一层目录下生成一个代码文件:Helloworld_generated.h

使用方法大概是这样的:
void testFlatBuffer()
//////////////////////////////////////////////////////////////////////// //
// 编码
//////////////////////////////////////////////////////////////////////// //

flatbuffers::FlatBufferBuilder builder;

int32_t id = 10086;
const char *str = "hello world";
int32_t opt = 10000;

#if 0
auto strName = builder.CreateString(str);
auto root = fbs::helloworld::CreateHelloWorld(builder, id, strName, opt);
#else
auto root = fbs::helloworld::CreateHelloWorldDirect(builder, id, str, opt);
#endif

#if 0
builder.Finish(root);
#else
FinishHelloWorldBuffer(builder, root);
#endif

auto p = builder.GetBufferPointer();
auto sz = builder.GetSize();

auto bufferpointer =
reinterpret_cast< const char *>(builder.GetBufferPointer());
std:: string buffer;
buffer.assign(bufferpointer, bufferpointer + builder.GetSize());
size_t n = buffer.length();

builder.ReleaseBufferPointer();

//////////////////////////////////////////////////////////////////////// //
// 解码
//////////////////////////////////////////////////////////////////////// //
#if 0
auto decode = flatbuffers::GetRoot<proto::helloworld::HelloWorld>(buffer.c_str());
#else
auto decode = fbs::helloworld::GetHelloWorld(buffer.c_str());
#endif
assert(decode->id() == 10086);
assert( strcmp(decode->str()->c_str(), "hello world") == 0);
assert(decode->opt() == 10000);



ByteBuffer
关于这个嘛,看以前的文章:http://www.cppblog.com/tx7do/archive/2015/06/12/145865.html
协议定义如下:
#include "ByteBuffer.h"

// 声明序列化
#define NET_APPEND(STRUCT_TYPE)\
static ByteBuffer& operator <<(ByteBuffer& lht, const STRUCT_TYPE& rht)

// 声明解序列化
#define NET_READ(STRUCT_TYPE)\
static const ByteBuffer& operator >>( const ByteBuffer& lht, STRUCT_TYPE& rht)

namespace bb
namespace helloworld
struct CMD_HelloWorld
int32            id; // id
std:: string str; // str
int32            opt; // optional field

CMD_HelloWorld()
this ->id = 0;
this ->opt = 0;
this ->str.clear();
NET_APPEND(CMD_HelloWorld)
lht << rht.id
<< rht.str
<< rht.opt;
return lht;
NET_READ(CMD_HelloWorld)
lht >> rht.id
>> rht.str
>> rht.opt;
return lht;
使用的代码是这样的:
void testByteBuffer()
//////////////////////////////////////////////////////////////////////// //
// 编码
//////////////////////////////////////////////////////////////////////// //
bb::helloworld::CMD_HelloWorld msg_encode;
msg_encode.id = 10086;
msg_encode.str = "hello world";
msg_encode.opt = 10000;

ByteBuffer sendBuffer;
sendBuffer.clear();
sendBuffer << msg_encode;
auto p = sendBuffer.contents();
auto sz = sendBuffer.size();
//////////////////////////////////////////////////////////////////////// //
// 解码
//////////////////////////////////////////////////////////////////////// //
ByteBuffer recvBuffer;
recvBuffer.clear();
recvBuffer.append((uint8*)p, sz);
bb::helloworld::CMD_HelloWorld msg_decode;
recvBuffer >> msg_decode;

assert(msg_decode.id == 10086);
assert(strcmp(msg_decode.str.c_str(), "hello world") == 0);
assert(msg_decode.opt == 10000);
相比PB来说,FBS不需要额外的指定一个外部的缓存,它内置了一个缓存,大概这就是它快的缘故吧.
序列化之后的空间占用结果是: protobuf: 19 flatbuffers: 48 bytebuffer: 20.
从空间上看,FBS是并不占优啊.
以前,一直使用的是ByteBuffer,因为简单,而且无论是从空间上还是时间上都还算划算.但是要手写序列化的代码,相对来说,比较烦人.所以还是PB,FBS这样的利用IDL自动生成代码的方式方便.
PB现在支持几乎所有语言,是一个相当成熟的解决方案了.
FBS就目前来说,支持的语言并不多,官方只支持:C++ , Java , C# , Go , Python , JS , C , PHP , Ruby.
PB的IDL各大编辑器几乎都支持它的语法染色,然而FBS却并没有,这个看起来也很让人蛋疼.
PB相对慢,但是空间占用小,它更适合外网传输,并且对时间并不是那么要求高的应用;
FBS相对快,但是空间占用较大,它在RPC,内网传输,以及对时间要求很高的场景上会很适合,在手机登移动平台下,计算性能不高的场景下也是适合的.
总的来说,我要做网游的通讯协议,还是PB更加的适合.
FBS更适合的是Android,iOS这样的移动平台,因为它们对性能要求比较高,使用FBS能够有效的提高性能.
IM通讯协议专题学习(四):从Base64到Protobuf,详解Protobuf的数据编码原理
本篇将从Base64再到Base128编码,带你一起从底层来理解Protobuf的数据编码原理。 本文结构总体与 Protobuf 官方文档相似,不少内容也来自官方文档,并在官方文档的基础上添加作者理解的内容(确保不那么枯燥),如有出入请以官方文档为准。
Java序列化案例demo(包含Kryo、JDK原生、Protobuf、ProtoStuff以及hessian)(二)
Java序列化案例demo(包含Kryo、JDK原生、Protobuf、ProtoStuff以及hessian)(二)
Java序列化案例demo(包含Kryo、JDK原生、Protobuf、ProtoStuff以及hessian)(一)
Java序列化案例demo(包含Kryo、JDK原生、Protobuf、ProtoStuff以及hessian)(一)
【Android Protobuf 序列化】Protobuf 使用 ( Protobuf 序列化 | Protobuf 反序列化 )
【Android Protobuf 序列化】Protobuf 使用 ( Protobuf 序列化 | Protobuf 反序列化 )
【Android Protobuf 序列化】Protobuf 使用 ( Protobuf 源码分析 | 创建 Protobuf 对象 )
【Android Protobuf 序列化】Protobuf 使用 ( Protobuf 源码分析 | 创建 Protobuf 对象 )
【Android Protobuf 序列化】Protobuf 使用 ( Protobuf 使用文档 | 创建 Protobuf 源文件 | Protobuf 语法 )
【Android Protobuf 序列化】Protobuf 使用 ( Protobuf 使用文档 | 创建 Protobuf 源文件 | Protobuf 语法 )
【Android Protobuf 序列化】Protobuf 简介 ( Protobuf 项目简介 | Protobuf 优缺点分析 )
【Android Protobuf 序列化】Protobuf 简介 ( Protobuf 项目简介 | Protobuf 优缺点分析 )
【Android Protobuf 序列化】Protobuf 性能测试 ( fastjson 序列化与反序列化 | gson 序列化与反序列化 | 三种序列化与反序列化性能对比 )(二)
【Android Protobuf 序列化】Protobuf 性能测试 ( fastjson 序列化与反序列化 | gson 序列化与反序列化 | 三种序列化与反序列化性能对比 )(二)
【Android Protobuf 序列化】Protobuf 性能测试 ( fastjson 序列化与反序列化 | gson 序列化与反序列化 | 三种序列化与反序列化性能对比 )(一)
【Android Protobuf 序列化】Protobuf 性能测试 ( fastjson 序列化与反序列化 | gson 序列化与反序列化 | 三种序列化与反序列化性能对比 )(一)
【优雅代码】12-hessian、kryo、json序列化对比
平常我们在使用rpc调用或者将其持久化到数据库的时候则需要将对象或者文件或者图片等数据将其转为二进制字节数据,那么各自的优劣是什么呢。
Netty 使用 Protobuf 序列化,太强大了!
我们来使用Protobuf进行序列化,它和XML,json一样都有自己的语法,xml的后缀是.xml,json文件的后缀是.json,自然Protobuf文件的后缀就是.proto(哈哈,当然不是全称)。