syntax = "proto2";
package proto.helloworld;
message HelloWorld
{
required int32 id = 1; // id
required string str = 2; // str
optional int32 opt = 3; // optional field
}
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%
@cls
:: /////////////////////////////////////////////////////////////
::
:: 编译Protobuf协议
::
:: /////////////////////////////////////////////////////////////
protoc --version
protoc --cpp_out=../ ./Helloworld.proto
pause
执行以上的批处理文件,将会在其目录的上一层目录下生成两个源码文件:Helloworld.pb.h和Helloworld.pb.cc.@cls
:: /////////////////////////////////////////////////////////////
::
:: 编译Protobuf协议
::
:: /////////////////////////////////////////////////////////////
protoc --version
protoc --cpp_out=../ ./Helloworld.proto
pause
将生成的这俩文件拖进工程里面去,至于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);
}
{
//////////////////////////////////////////////////////////////////////////
// 编码
//////////////////////////////////////////////////////////////////////////
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);
}
{
//////////////////////////////////////////////////////////////////////////
// 编码
//////////////////////////////////////////////////////////////////////////
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的基本用法了.{
//////////////////////////////////////////////////////////////////////////
// 编码
//////////////////////////////////////////////////////////////////////////
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);
}
FlatBuffers
FlatBuffers支持将ProtoBuf的IDL转换为自己的IDL,只需要简单的一行命令:
Helloworld.fbs它看起来像是这样的:
我们可以用下面这个bat去生成代码:
使用方法大概是这样的:
ByteBuffer
关于这个嘛,看以前的文章:http://www.cppblog.com/tx7do/archive/2015/06/12/145865.html
协议定义如下:
总结
相比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能够有效的提高性能.
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修改过的.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;
我们可以用下面这个bat去生成代码:
flatc.exe --cpp -o ../ Helloworld.fbs
pause
执行之后,它将在当前目录的上一层目录下生成一个代码文件:Helloworld_generated.hpause
使用方法大概是这样的:
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);
}
{
//////////////////////////////////////////////////////////////////////////
// 编码
//////////////////////////////////////////////////////////////////////////
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;
};
}
}
使用的代码是这样的:// 声明序列化
#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);
}
{
//////////////////////////////////////////////////////////////////////////
// 编码
//////////////////////////////////////////////////////////////////////////
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能够有效的提高性能.