c++服务器protobuf使用
环境配置和使用
一:准备工作
1.vs2012
2.下载protobuf,我使用的版本是protobuf-2.6.1,git地址: https://github.com/google/protobuf.git
3.编译,会在vsprojects/Debug/下生成两个静态库libprotobuf.lib、libprotoc.lib和一个protoc.exe
二:配置c++环境
1.源文件包括:protobuf解压目录下的src目录和编译后的生成的libprotobuf.lib、libprotoc.lib两个库,把它们拷贝出来放到我们自己工程根目录。目录结构如这样:extension[lib(libprotobuf.lib,libprotoc.lib), src(…)]
2.工程右击–>属性–>C/C++–>常规–>附加包含目录,添加extension\src目录。
3.同样…–>链接器–>常规–>附加库目录,添加extension\lib目录。
4.同样…->链接器–>输入–>附加依赖项,添加libprotobuf.lib、libprotoc.lib这两个库。
三:proto定义和转目标代码
1.简单proto文件定义
syntax = "proto2"; //proto版本(2.x)
package test; //包名//消息定义
message People{ required string name = 1; required int32 age = 2; optional string email = 3;
}
2.转成c++文件使用
使用开始编译出来的protoc.exe工具,可以把proto编译生成不同的语言版本。这里我们一般使用.bat批处理,把.exe,.proto和.bat都放一个目录下,点击.bat自动编译目录下所有.proto文件。
转c++目标语言批处理如下:
@echo off
for /r %%i in (*.proto) do ( echo %%~ni.protoprotoc.exe --cpp_out=../../server/proto/ ./%%~ni.proto
)
pause
运行后,会在../../server/proto/目录下生成对应的xx.pb.h,xx.pb.cc文件。这就是c++项目中我们要使用的协议文件了,把它们全部导入我们的项目中。
四:使用示例
1.序列化
test::People* p = new test::People();
p->set_email("cxx@gmail.com");
p->set_id(10086);
p->set_name("cxx");int buffsize = p->ByteSize();
void* buff = malloc(buffsize);
p->SerializeToArray(buff, buffsize);
2.反序列化
test::People person;
person.ParseFromArray(buff, length);string name = person.name();
int age = person.age();
string email = person.email();
注意:proto中定义的字段名大小写,转到c++全部会自动转为小写形式。
实际网络游戏proto定义格式
实际网络游戏开发中,我们使用proto定义消息一般都是通过主协议号、子协议号来映射协议的。即每条协议都有主协议号和子协议号来唯一索引它。定义上一般按功能模块划分,主协议指示某个大模块,子协议指示大模块下的具体协议。
下面我们来看看实际项目中proto应该怎么定义框架:
1.主协议proto定义(协议命名格式:enumName_childModelName)
//Pmd.protosyntax = "proto2"; //指定proto版本2.x
package PlatPmd; //包名,c++中对应namespaceenum PlatCommand
{PlatCommand_NullPmd = 0; //基础模块(网络包结构、心跳等协议)PlatCommond_LoginPmd = 10; //登入模块
}
2.子协议proto定义(协议命名格式:XxxModelName_CS[SC],对应发送/回复名字前缀统一)
//基础模块NullPmd.protosyntax = "proto2";
package PlatPmd;message NullPmd
{enum Param{NetPackageNullPmd_CS = 1;NetTickNullPmd_SC = 2;}
}message NetPackageNullPmd_CS
{optional uint32 byCmd = 1;optional uint32 byParam = 2;optional uint32 seq = 3; // 发送序列号,断线重连用 optional uint64 fid = 4; // server used for forwardoptional bytes data = 5; // dataoptional uint64 prototype = 6; // 0:proto,1:json,2:httpoptional uint32 bitmask = 7; // FrameHeader位枚举optional uint32 time = 8; // client use
}message NetTickNullPmd_SC
{optional uint32 requesttime = 1; // 对方的请求时间原封返回optional uint32 mytime = 2; // 当前应答的本地时间,秒,必须填,用来防止加速
}
//登入模块LoginPmd.protosyntax = "proto2";
package PlatPmd;message LoginPmd
{enum Param{StartLoginLoginPmd_CS = 1;StartLoginLoginPmd_SC = 2;}
}enum VerifyReturnReason
{LoginOk = 0; // 登录成功TokenFindError = 1; // 服务器没有tokenTokenDiffError = 2; // token错误VersionError = 3; // 版本验证
}// 登入
message StartLoginLoginPmd_CS
{required string account = 1; // 平台账号required string token = 2; // token 可以是第三方认证required uint32 version = 3; // 当前客户端login版本号Version_Loginoptional uint32 gameid = 4; // if filled, will send ZoneInfoListLoginUserPmd_S for select, else auto select zoneoptional string mid = 5; // 机器码optional uint32 platid = 6; // 平台编号optional uint32 zoneid = 7; // if filled, will auto login UserLoginRequestLoginUserPmd_C, UserLoginTokenLoginSmd_SCoptional uint32 gameversion = 8; // 当前客户端game版本号Version_Loginoptional string compress = 9; // 压缩算法 optional string encrypt = 10; // 加密算法optional string encryptkey = 11; // 加密key
}//登录返回
message StartLoginLoginPmd_SC
{required VerifyReturnReason retcode = 1; // 返回值optional string desc = 2; // 返回错误描述,正确时不填
}
注意命名规则的定义!
协议映射关系
1.使用示例
//注册proto协议
MessageSerializer serializer;
if(registerMessage(serializer) == false)return -1;//通过主、子协议获取对应协议对象,然后反序列化
google::protobuf::Message* prototype = serializer.getMessageByCmdParam(byCmd, byParam)
google::protobuf::Message* message = prototype->New();
message->ParseFromArray(buff, buffsize);
2.相关源代码
//Message.cpp 初始化proto协议文件#include "proto/Pmd.pb.h"
#include "proto/NullPmd.pb.h"
#include "proto/LoginPmd.pb.h"//初始化子协议信息
void initParamDescriptor()
{PlatPmd::NullPmd_Param_descriptor();PlatPmd::LoginPmd_Param_descriptor();
}//注册proto协议(初始化入口,映射信息保存在serializer对象中)
bool registerMessage(MessageSerializer* serializer)
{initParamDescriptor();//主协议信息if (serializer->Register(PlatPmd::PlatCommand_descriptor(), "PlatPmd") == false)return false;return true;
}
//MessageSerializer.h proto协议映射绑定#pragma onceclass MessageSerializer
{
public:MessageSerializer();private://保存所有协议结构对象const google::protobuf::Message* m_unserializeTable[65536];public:/** 注册proto协议.* @param byCmdEnum 主协议enum信息 ns 命名空间* @return true or false.*/bool Register(const google::protobuf::EnumDescriptor* byCmdEnum, const std::string ns);/** 具体一条协议注册.* @param byCmd 主协议号 byParam 子协议号 typeDescriptor 具体一条协议信息* @return true or false.*/bool Register(unsigned char byCmd, unsigned char byParam, const google::protobuf::Descriptor* typeDescriptor);/** 通过主、子协议号获取协议对象.* @param byCmd 主协议号 byParam 子协议号* @return google::protobuf::Message.*/google::protobuf::Message* getMessageByCmdParam(unsigned char byCmd, unsigned char byParam);
};//Message.cpp 接口
void initParamDescriptor();
bool registerMessage(MessageSerializer* serializer);
//MessageSerializer.cpp#include "MessageDispatcher.h"MessageSerializer::MessageSerializer()
{memset(m_unserializeTable, 0, sizeof(m_unserializeTable));
}Message* MessageSerializer::getMessageByCmdParam(unsigned char byCmd, unsigned char byParam)
{unsigned int uMsgID = (byCmd << 8) + byParam;return m_unserializeTable[uMsgID];
}bool MessageSerializer::Register(const EnumDescriptor* byCmdEnum,const std::string ns)
{if(byCmdEnum == NULL){printError("MessageSerializer::Register insert err");return false;}for(int i = 0; i < byCmdEnum->value_count(); i++){const EnumValueDescriptor* item = byCmdEnum->value(i);const int c = item->number();std::size_t found = item->name().find_last_of("_");std::string cmdname = item->name().substr(found+1);const std::string paramtype = ns + "." + cmdname + ".Param";printInfo("MessageSerializer::Register byCmdEnum:%d,%s,%s",c,paramtype.c_str(),item->name().c_str());const EnumDescriptor* byParamEnum = DescriptorPool::generated_pool()->FindEnumTypeByName(paramtype);if(byParamEnum == NULL){printError("MessageSerializer::Register err:%d,%s,%s",c,paramtype.c_str(),item->name().c_str());return false;}for(int i = 0; i < byParamEnum->value_count(); i++){const EnumValueDescriptor* item = byParamEnum->value(i);if(c > 0 && c < 200 && item->name().find(cmdname.c_str()) == std::string::npos){printError("MessageSerializer::Register name err:%d,%s,%s,需要名字严格匹配规则",c,cmdname.c_str(),item->name().c_str());return false;}const int t = item->number();printInfo("MessageSerializer::Register byParamEnum:[%d,%d],%s,%s",c,t,byParamEnum->full_name().c_str(),item->name().c_str());const Descriptor* message = DescriptorPool::generated_pool()->FindMessageTypeByName(ns + "." + item->name());if(message == NULL){printError("MessageSerializer::Register find err:[%d,%d],%s,%s",c,t,byParamEnum->full_name().c_str(),(ns + "." + item->name()).c_str());return false;}if(Register(c, t, message) == false){printError("MessageSerializer::Register insert err:[%d,%d],%s",c,t,byParamEnum->full_name().c_str());return false;}}}return true;
}bool MessageSerializer::Register(unsigned char byCmd, unsigned char byParam, const Descriptor* typeDescriptor)
{if(typeDescriptor == NULL){printError("MessageSerializer::Register err");return false;}const Message* prototype = MessageFactory::generated_factory()->GetPrototype(typeDescriptor);if(m_unserializeTable[(byCmd<<8) + byParam] != NULL && m_unserializeTable[(byCmd<<8) + byParam] != prototype){printError("MessageSerializer::Register insert err[%u,%u],%p,%p",byCmd,byParam,m_unserializeTable[(byCmd<<8) + byParam],prototype);return false;}m_unserializeTable[(byCmd<<8) + byParam] = prototype;return true;
}
c++服务器protobuf使用相关推荐
- 解压并安装protobuf库_golang-leaf服务器-protobuf的安装
json和protobuf 2种,我这里选择protobuf 源码地址: golang/protobufgithub.com 1. 安装编译器protoc protocolbuffers/proto ...
- 阿里P7 java架构师 springcloud、jvm、netty 、redis、数据结构、分布式高并发 视频 下载分享
作为普通java码农,你是否因为日复一日地写着业务代码无法深入了解前沿技术而感到焦虑: 作为经验尚浅的架构负责人,你是否在为新项目如何做到高并发秒杀.熔断.限流而感到无所适从: 作为一个跳槽求职者,你 ...
- 【C++】C++的工具库
目录 处理高并发的库 日志 PRC 嵌入式文件系统 代码质量 深度学习库 HTTP 网络库 单元测试 性能测试 文件压缩 下次造轮子前先看看现有的轮子吧 值得学习的C语言开源项目 - 1. Webbe ...
- 【Android Protobuf 序列化】Protobuf 服务器与客户端通信 ( TCP 通信中使用 Protobuf )
文章目录 一.TCP 粘包和分包 二.TCP 粘包和分包解决方案 三.客户端 Android 应用使用 Protobuf 四.服务器端 Java 服务器使用 Protobuf 五.参考资料 一.TCP ...
- (十五)nodejs循序渐进-高性能游戏服务器框架pomelo之Protobuf模块
消息压缩 在实际编程中,为了减少数据传输带宽的消耗,提高传输效率,pomelo提供了对消息的压缩,包括基于字典的对route的压缩和基于protobuf的对具体传输数据的压缩. route压缩 在实际 ...
- 什么是Protocol Buffers / protobuf / protobuffer?一种服务器和客户端的消息交互方式
Table of Contents Protocol Buffers 定义消息类型 指定字段类型 分配字段编号 指定字段规则 添加更多消息类型 添加评论 保留字段 您产生了什么.proto? 标量值类 ...
- Netty实战:Springboot+Netty+protobuf开发高性能服务器 (附源码下载)
Springboot-cli 开发脚手架系列 Netty系列:Springboot使用Netty集成protobuf开发高性能服务器 文章目录 Springboot-cli 开发脚手架系列 简介 1. ...
- java 游戏 protobuf,Unity 3D Protobuf的使用 信息的简单发送 并在Java服务器上接受显示...
做过有网络相关游戏的人都知道protobuf,google的一套开源工具,用于发送信息的序列化和反序列化,是一个非常重要的网络工具.下面给大家介绍的是使用Protobuf用来实现信息的发送,并让信息在 ...
- Skynet 服务器开发 (四) 使用pbc(protobuf)
转载自:https://blog.csdn.net/u010693827/article/details/85689483 引言: 假如我们要建立的skynet服务器与客户端的连接方式为长连接,且选择 ...
最新文章
- tesseract 识别中文字符
- matlab 平滑曲线连接_平滑轨迹插值方法之多项式插值(附代码)
- 利用WebBrowser实现Web打印的分析
- vscode源码分析【一】从源码运行vscode
- matlab 7.0安装教程
- 运行命令、文件扩展名速查、Windows运行命令大全
- excel教程自学网_超实用!良心推荐15个神级自学网站,内容全面质量又高
- 关于lora和lorawan所涉及的名词解释
- 冒泡排序程序java_冒泡排序Java程序
- 诡辩六论——微信陌陌如何话题不断的聊天
- UE4--局域网多人联机
- 尝试用visio画个等边三角形
- Flowable工作流之核心流程操作的本质
- 是什么让区块链游戏变的“好玩”
- 网易我的世界java怎么开光追_《我的世界》如何开启光线追踪?
- 区块链之联盟链---认识Fabric
- 百度云盘不限速下载工具(附带开源源码)
- OSChina 周三乱弹 —— 请叫我马化喵
- 各种中文乱码的解决方法 (转)
- 《码农翻身》读后感---程序员的潜规则