框架的前后端通讯
protobuf
1.什么是protobuf?
protobuf是Google公司提出的一种轻便高效的结构化数据存储格式,常用于结构化数据的序列化,具有语言无关、平台无关、可扩展性特性,常用于通讯协议、服务端数据交换场景
2.为什么要使用protobuf作为数据传输协议?
protobuf优势:
1)类型安全
2)易用性好
3)序列化/反序列性能好
4)兼容性好
5)不仅可以定义结构体,还可以定义rpc服务接口
劣势:
1)可读性较差:没有schema的情况下,难以阅读和编辑。
2)灵活性较差:无法动态修改schema。
json: https://www.json.org/json-zh.html
优势:
1)可读性好:方便理解和编辑
2)易用性好:使用简单灵活性好:支持动态修改schema
劣势:序列化/反序列化性能差编码问题导致解析失败之类的
PS:在我做过的几款项目中,前面两款都是用protobuf消息协议,后面的项目使用的是Json,可能是protobuf用惯了,
在使用Json时感受到了其反序列化时的痛苦,而且有时候后端偷偷删掉了某个字段还会导致解析失败,简直是头疼。
3.Protobuf为什么这么快?
一个protobuf消息:
1
2
3
4
5
6
7message Person
{
required int32 Id = 1;
required string Name = 2;
required string Hobby = 3;
required string Introduction = 4;
}
假如一样的消息通过Json来传,每个消息体需要把某个数据是属于哪个字段的信息一起下发(例如 id : 1), 而protobuf不需要将这个字段名一起带到消息中,那它是如何区分数据属于哪个字段呢:
现在有个Person消息是这样的 id = 1, Name = Cwp, Hobby = Play tennis, Introduction = Hello word
protobuf会将这个消息以这种形式写入
Tag1TagCwpTagPlay tennisTagHello Word
Tag是如何来的?
1 |
|
fieldNumer:即消息定义时 “=” 号右边的数字,这也就是为什么定义完一个消息后就不允许再修改这个值的原因(修改后Tag也会变,从而导致解析失败)
wireType:字段类型,即int32, string等,protobuf支持的类型如下图, 0-5转为二进制最高用3位就能表示全,故需要将fieldNumber左移三位,后三位用wireType的二进制补全,这样解析的时候只需要取后三位就能知道类型
Tag分隔符为一个字节,如果传输的内容中出现相同的字节,那么不会导致解析错误吗?
实际上是不会的,protobuf避免了这种情况:
例如有个数据为267, 用二进制表示是
00000000 00000000 00000001 00001011 (256 + 8 + 2 + 1)
其中前两个字节都是0,属于无效字节,protobuf会将其舍弃,上述字节变成以下形式
10001011 00000010 ,每个字节的后面七位才是属于数据本身, 第一个字节(10001011)中后七位取的是原数据二进制的低
七位(大小端?,这里没有进一步了解,先记录下来), 取完后发现再往前还有有效数据(往前还有非0的数据),所以第8位置
为1, 1代表下一个字节(00000010)还是属于该数据, 第二个字节(00000010)是从原二进制右移七位后截取低7位截取的,
取完后此时再往前的数据都是0,所以第8位置0,这个时候代表下一个字节的数据已经不是该数据(267)了。
另外,protobuf是以Tag-Length-Value形式编码的(Tag为分隔符,Length为数据长度,Value则是数据),那么如果一开始
是要传输一个数字,取到第一个tag的时候,解析出它的fieldNumber和writeType, 如果高位为1则表示下一个字节还是该数字,
如果为0则表示下一个字节是Tag. 而如果一开始传输的是一个字符串,拿到Tag后,就知道下一个数据是一个字符串,所以下一
个字节开始作为Length解析,遇到0的话就知道value的长度了(字节解析后的值),然后根据这个长度去取出字符串,取完后
下一个字节就是Tag了,以此类推, protobuf永远知道哪一个字节是Tag
以上内容参考自:
B站 Protobuf为什么这么快?
知乎Protobuf编码原理
在框架中使用protobuf
在 工程目录/Proto目录 下有四个文件:
InnerMessage.proto :服务器内部通讯的消息数据
MongoMessage.proto :服务器内部通讯的消息数据,可以传输entity
OuterMessage.proto : 用来定义客户端发送到服务端的消息数据
win_startProtoExport.bat :定义完消息数据后执行这个文件,会生成对应的类在项目代码中
OuterMessage
1 |
|
ET中前后端通讯的消息分为两类
1) 客户端可以直接发送给服务端的消息
2) 客户端消息必须通过网管服转发到服务端,此类消息在框架中被称为ActorLocation消息
框架中如何使用
客户端发送消息的伪代码如下:
1 |
|
后端处理消息的伪代码如下:
1 |
|
客户端处理后端主动推送消息的伪代码如下:
1 |
|
注:上面的C2R_Login是IActorReueqst类型,在ET中还有一种IActorLocationRequest类型,这两种类型的区分在这里先不记录,在
后面开发有遇到的时候再研究其区分。