美文网首页
Protobuf消息规则

Protobuf消息规则

作者: boyiis | 来源:发表于2020-12-29 17:33 被阅读0次

消息的大致类型

网络通信涉及到消息的定义,不管是直接使用二进制格式,还是xml、json等字符串格式。消息都可以大体的分为3大消息类型:

  • 请求消息
  • 应答消息
  • 命令消息
    一般情况下,每个消息还会包含一个序列号、和一个能够唯一区分消息类型的类型定义。
原则一:使用enum定义消息类型

为每个系统都定义一个HeadType枚举。包含系统用到的所有消息的枚举类型

enum HeadType
{
  Login_Request = 1;//登陆请求
  Login_Response = 2;//登录响应
  Logout_Request = 3;//退出请求
  Logout_Response = 4;
  Keepalive_Request = 5;//心跳请求ping;
  Keepalive_Response = 6;
  Message_Request = 7;//消息请求;
  Message_Response = 8;//消息回执;
  Message_Notification = 9;//通知消息
}
原则二:一个protobuf message对应一类消息

会为每个具有消息体的消息定义一个对应的protobuf message
例如Login_Request会有一个对应的LoginRequest消息

/*登录信息*/
// LoginRequest对应的HeadType为Login_Request
// 消息名称去掉下划线,更加符合Java 的类名规范
message LoginRequest{
    required string uid = 1;        // 用户唯一id
    required string deviceId = 2;     // 设备ID
    required string token = 3;       // 用户token
    optional uint32 platform = 4;      //客户端平台 windows、mac、android、ios、web
    optional string app_version = 5;    // APP版本号
}​
原则三:应答消息需要成功标记和应答序号

对于应答消息,并非总是成功的,因此在应答消息中还会包含另外2个字段。

  • 一个用于描述应答是否成功,一个用于描述失败时的字符串信息。
  • 对于有多个应答的消息来说,可能会包含是否为最后一个应答消息的标识——应答的序号(类似与网络数据包被分包以后,协议要合并时,需要知道分片在包中的具体位置)。
    因此Response看起来是这样:
/*聊天响应*/
message MessageResponse
{
    required bool result = 1; //true表示发送成功,false表示发送失败
    required uint32 code = 2;   //错误码
    required string info = 3;   //错误描述
    required uint32 expose = 4; //错误描述是否提示给用户:1 提示;0 不提示
    required bool last_block = 5;
    required fixed32 block_index = 6;
}
原则四:编解码从顶层消息开始

最后我会定义一个大消息,把所有的消息类型,全部封装在一起,让后在通信的时候都从顶层消息开始编解码。大消息看起来像下面这样。

/*顶层消息*/
//顶层消息是一种嵌套消息,嵌套了各种类型消息
//内部的消息类型,全部使用optional字段
//根据消息类型 type的值,最多只有一个有效
message Message
{
 required HeadType type = 1; //消息类型
 required fixed32 sequence = 2;//消息系列号
 fixed32  session_id = 3;
 optional LoginRequest loginRequest = 4;
 optional LoginResponse loginResponse = 5;
 optional MessageRequest messageRequest = 6;
 optional MessageResponse messageResponse = 7;
 optional MessageNotification notification = 8;
}
原则五:TCP 消息需要进行二进制包装

用于UDP的时候比较简单,因为每个数据包就是一个独立的Message消息,可以直接解码,或者编码后直接发送。

但是如果是使用于TCP的时候,由于涉及到粘包、拆包等处理,而且Message消息里面也没有包含长度相关的字段(不好处理),因此把Message编码后的消息嵌入另外一个二进制消息中。

使用4字节消息长度+Message(二进制数据)+(2字节CRC校验(可选))

其中4字节的内容,只包含Message的长度,不包含自身和CRC的长度。如果需要也可以包含,当要记得通信双方必须一致。

协议接口文件完整 实例

下面是一个 为疯狂创客圈 100W*100级 分布式 IM项目定义 google protobuf 的协议接口文件

//定义protobuf的包名称空间
​
option java_package = "com.crazymakercircle.chat.common.bean.msg";
​
// 消息体名称
option java_outer_classname = "ProtoMsg";
​
​
enum HeadType
{
  LOGIN_REQUEST = 1;//登陆请求
  LOGIN_RESPONSE = 2;//登录响应
  LOGOUT_REQUEST = 3;//退出请求
  LOGOUT_RESPONSE = 4;
  KEEPALIVE_REQUEST = 5;//心跳请求PING;
  KEEPALIVE_RESPONSE = 6;
  MESSAGE_REQUEST = 7;//消息请求;
  MESSAGE_RESPONSE = 8;//消息回执;
  MESSAGE_NOTIFICATION = 9;//通知消息
}
​
/*登录信息*/
// LoginRequest对应的HeadType为Login_Request
// 消息名称去掉下划线,更加符合Java 的类名规范
message LoginRequest{
    required string uid = 1;        // 用户唯一id
    required string deviceId = 2;     // 设备ID
    required string token = 3;       // 用户token
    optional uint32 platform = 4;      //客户端平台 windows、mac、android、ios、web
    optional string app_version = 5;    // APP版本号
}
​
//token说明: 账号服务器登录时生成的Token
​
/*登录响应*/
message LoginResponse{
    required bool  result = 1; //true 表示成功,false表示失败
    required uint32 code = 2;   //错误码
    required string info = 3;   //错误描述
    required uint32 expose = 4; //错误描述是否提示给用户:1 提示;0 不提示
    required string session_id = 5;     //sessionId
}
​
​
​
/*聊天消息*/
message MessageRequest{
     uint64 msg_id = 1;     //消息id
     string from = 2;       //发送方uId
     string to = 3;         //接收方uId
     uint64 time = 4;       //时间戳(单位:毫秒)
     required uint32 msg_type = 5;  //消息类型  1:纯文本  2:音频 3:视频 4:地理位置 5:其他
   required string session_id = 6;      //sessionId
   string content = 7;  //消息内容
     string url = 8;        //多媒体地址
     string property = 9;   //附加属性
     string from_nick = 10; //发送者昵称
     optional string json = 11;     //附加的json串
}
​
/*聊天响应*/
message MessageResponse
{
    required bool result = 1; //true表示发送成功,false表示发送失败
    required uint32 code = 2;   //错误码
    required string info = 3;   //错误描述
    required uint32 expose = 4; //错误描述是否提示给用户:1 提示;0 不提示
    required bool last_block = 5;
    required fixed32 block_index = 6;
}
​
/*通知消息*/
message MessageNotification
{
 required uint32 msg_type = 1;  //通知类型 1 上线 2 下线 ...
 required bytes sender = 2;
 required string json = 3;
 required string timestamp = 4;
}
​
/*顶层消息*/
//顶层消息是一种嵌套消息,嵌套了各种类型消息
//内部的消息类型,全部使用optional字段
//根据消息类型 type的值,最多只有一个有效
message Message
{
 required HeadType type = 1; //消息类型
 required uint64   sequence = 2;//消息系列号
 required fixed32  session_id = 3;
 optional LoginRequest loginRequest = 4;
 optional LoginResponse loginResponse = 5;
 optional MessageRequest messageRequest = 6;
 optional MessageResponse messageResponse = 7;
 optional MessageNotification notification = 8;
}
​
​
// sequence 消息系列号
// 主要用于Request和Response,Response的值必须和Request相同,使得发送端可以进行事务匹配处理

相关文章

  • Protobuf消息规则

    消息的大致类型 网络通信涉及到消息的定义,不管是直接使用二进制格式,还是xml、json等字符串格式。消息都可以大...

  • Protobuf的Enum枚举类型不能同名?

    Protobuf的Enum枚举类型不能同名? 报错 原因 protobuf使用类似c的枚举规则,不允许枚举中出现同...

  • how to use protobuf Reflection?

    最近工作中,需要做一些消息动态解析,因为使用的 protobuf,考虑使用protobuf的反射特性。 1 ref...

  • Protobuf消息定义最佳实践

    Protobuf 是谷歌开源的序列化和反序列化工具,比Json压缩效率更高。越来越广泛应用于RPC服务中。使用Pr...

  • MMKV的原理与实现(二)

    MMKV的原理与实现(二) 上一篇讲了MMKV的存储原理以及protobuf编码的规则,并以一个整数的编码规则举例...

  • mac上安装Protobuf

    为什么要安装protobuf 什么是protobuf 怎么判断有没有安装过protobuf? 安装protobuf...

  • protobuf使用

    protobuf的使用 protobuf .proto文件 idea安装protobuf插件 syntax = ...

  • Google Protocol Buffers 数据交换协议

    protobuf 简介 protobuf是什么 protobuf(Protocol Buffers)是Google...

  • protobuf

    protobuf是什么#### protobuf是"Protocol Buffers"的简称。protobuf是一...

  • 根据消息名称解析protobuf格式

    最近在解析一个网站的websocket时,发现使用的是protobuf,每个消息都使用一个统一的Wrapper封装...

网友评论

      本文标题:Protobuf消息规则

      本文链接:https://www.haomeiwen.com/subject/kxnfoktx.html