以上最底层的tcp流的单个报文的格式

以下是经过粘包处理后的tcp字节流转为protobuf报文后的protobuf报文层级关系图

这些报文均由定义好的.proto文件通过protoc.exe来自动生成相应的.go文件,特此感谢google爸爸开源了这么好的项目(虽然并不能传到google爸爸的耳朵里),让我这个渣渣能有口饭吃(* ̄3 ̄)╭

报文层级1文件Tcp.proto:

这个proto文件里的报文都是与“单个服务器APP结构”图里的网络层相关的

TCPTransferMsg是用于数据传输的报文。

在发送的数据最后一步抵达网络层时,是对TCPTransferMsg使用proto.Marshal进行序列化,然后获取其长度length。以四个字节作为TCP流的报文头,这四个字节的组成一个int32类型的变量,这个变量值为length。

下图就是一个完整报文的TCP字节流的示意图

发送的过程就是将一个TCPTransferMsg类型的变量通过proto.Marshal序列化成一个[]byte类型的buff变量后作为body与其buff长度作为head组成一个完整的tcp字节流转为大端网络字节序发送出去

接收过程则是,调用net.TCPConn.Read()先读取的头四个字节,作为head,然后再根据head的值取相应的长度的[]byte,然后将这个[]byte通过proto.Unmarshal逆序列化或者还原为一个TCPTransferMsg类型的变量

 

 

TCPSessionCome是当listenManager新建了一个ConnectionSession会话时将会向业务逻辑层发送一个带有connId作为ConnectionSession这个会话唯一标识符的TCPSessionCome类型的报文给业务逻辑层。GateApp和RouterApp这两个用到了listenManager模块的APP需要根据相应的业务逻辑处理这个报文

TCPSessionClose是当listenManager结束了一个ConnectionSession会话会向逻辑层发送的报文

TCPSessionKick是当逻辑层因为超时,顶号登录,IP黑白名单之类的业务需求而要结束某个会话的时候由逻辑层向网络层发送

报文层级2文件router.proto和gate.proto

首先router.proto和gate.proto里的所有报文都通过proto.Marshal的方式转化为TCPTransferMsg的报文

以gate.proto的PulseReq为例,不完整代码如下(完整的代码要调用bs_proto.SetBaseKindAndSubId()和bs_proto.CopyBaseExceptKindAndSubId()两个函数,这里为了直观理解这么写):

pulse :=new(bs_gate.PulseReq)
pulse.Base.KinId = bs_types.CMDKindId_IDKindGate
pulse.Base.SubId = bs_gate.CMDID_Gate_IDPulseReq
msg := new(bs_tcp.TCPTransferMsg)
msg.DataKindId = pulse.Base.KindId
msg.DataSubId = pulse.Base.SubId
buf, err := proto.Marshal(req)
if err != nil {return nil
} else {msg.Data = bufreturn msg
}

Gate.proto:

GateTransferData是这个proto文件里最重要的报文,功能是所有与用户客户端通信交互的报文都proto.Marshal到这个报文的TransferData.data成员变量中

Gate.proto的其他报文就不讲了,看注释很容易理解

Router.proto:

RouterTransferData是这个proto文件的最重要报文,功能与GateTransferData类似,不同之处是在于GateTransferData是用于GateApp与用户客户端之间的报文通信交互,

而RouterTransferData是用于服务端各个APP之间的通信交互,包括其他APP与GateAPP之间的交互也用RouterTransferData

RegisterAppReq是当一个除了routerAPP以外的APP启动时,DialManager模块拨号成功后立即向RouterApp发送的APP注册请求报文,包含了这个APP的apptype和appid的信息(apptype和appid在讲到types.proto会简单说明,然后后面讲解GateApp/RouterApp/以LoginApp为例子的业务型APP的时候可以看到其具体用法)

RegisterAppRsp是收到RegisterAppReq请求后的回复报文

PS:这里为什么要分为GateTransferData和RouterTransferData两个报文,而不是共用一个?

我的想法是(或者写这个C++框架的我的老大的说法是)

第一,在实际服务器部署时可能会出现routerApp监听的端口并不一定处于内网的安全范围内,可能暴露在外网中,可以被外网扫描。那么如果客户端和服务端用同一个报文,可能会收到直接伪造的报文的攻击。这样分成两种报文,如果外网向routerApp直接发送GateTransferData报文的时候proto.Unmarshal会解析失败并丢弃这个报文。至少增加了一点安全性。

第二,因为RouterTransferData报文比GateTransferData的size要稍微大一点,字段多了几个,把RouterTransferData转为GateTransferData向用户客户端发送时就可以去掉一些不必要字段

报文层级3的一般业务报文proto文件

这些报文在服务端APP相互传输的时候都先转为RouterTransferData报文,代码与上面的gate.proto报文转为TCPTransferMsg报文类似。这里就不重复写了

小结:一般业务逻辑的报文是在从服务端APP发往用户客户端时,从报文的角度看,过程是这样的:

BusinessMsg -> RouterTransferData -> TCPTransferMsg -----通过伟大的TCP/IP传到routerApp--->

TCPTransferMsg -> RouterTransferData -> TCPTransferMsg ----RouterApp通过伟大的TCP/IP传到GateApp---->TCPTransferMsg -> RouterTransferData -> GateTransferData -> TCPTransferMsg ------还是伟大的TCP/IP帮助我们神奇地传给了客户端------> TCPTransferMsg -> GateTransferData -> BusinessMsg -> 客户端根据BusinessMsg的值做相应的处理

Types.proto公共报文

多个proto可能公用的message或者enum都声明在types.proto里

BaseInfo是最典型的message,目前除了types.proto以外的所有proto文件里的报文都包含了types.BaseInfo类型的base成员变量,每个报文均有base。

以client.proto的LoginReq这个报文为例

kind_id = 1;表示这个报文的大类ID,对应的是types.proto的enum CMDKindId下面的值,通常每个enum CMDKindId对应一个proto。

LoginReq.base.kind_id = CMDKindId.IDKindClient

sub_id = 2;表示这个报文的小类ID,对应的是相应client.proto里定义的CMDID

LoginReq.base.sub_id = CMDID_Client.IDLoginReq

conn_id=3;表示这个报文来自于哪个ConnectionSession会话,在GateApp和RouterApp里有用到

gate_conn_id = 4;表示如果这个报文来自于客户端的话,那么gate_conn_id代表这个客户端在GateApp上对应的ConnectionSession会话

如果是发往客户端的话那么gate_conn_id指示GateApp发往哪个客户端,不过通常是通过userId来指示发往哪个客户端,当然发送登录请求的时候Gate并不知道客户端的userId,所以登录回复的时候要告诉GateApp是哪个gate_conn_id上的会话关联的用户客户端登录成功or失败了。如果登录成功了GateApp会记录下这个userId的值并将其与gate_conn_id关联绑定。

remote_add = 5;客户端的IP地址和端口,格式为“192.168.1.1:25536”的字符串

att_apptype = 6;表示这个报文关联的apptype,在收到的时候代表来源的apptype,在发出去的时候代表目标的apptype,对应的是types.proto的enum EnumAppType下面的值

当客户端发送登录请求LoginReq给服务端时

LoginReq.base.att_apptype = EnumAppType.Login

att_appid = 7; 表示这个报文关联的appid,在收到的时候代表来源的appid,在发出去的时候代表目标的appid。Appid是由程序猿自己整理的约定的表,只要值在uint32范围内就行了。但是值0,1,2是保留的有特殊含义的。对应的是types.proto的EnumAppId。Send2All = 1表示发送到所有,这里的所有指的并不是无差别的所有APP,而是根据AppType来发送的。就是属于同种appType的APP都发送,假设现在有3个GateApp和1个LoginApp连着Router,那么当DestAppType=Gate, DestAppId=Send2All的时候就是这个报文向3个GateApp都发送一次,而不会向LoginApp发送

Send2AnyOne=2是随机向确定appType的某个App发送,假设有2个LoginApp,那么就随机选一个发送,如果只有1个就只向这个发送

PS:建议APPID表10以下都不用了,说不定以后3,4,5也需要作为保留值。以现在APP举例,

GateApp的APPID = 101,RouterApp的APPID = 50,LoginApp的APPID = 30,这些数值由程序猿自己建个excel文档约定,只要保证不重复就行了。

不过实际上我对于GateApp这种会有多个的APP群,会把APPID从1000到1500的号码段都分配给他

我的邮箱:914509007@qq.com

railgun报文层级相关推荐

  1. 简单的golang游戏服务器框架《railgun》的文档目录索引

    使用golang写的框架.使用的是require response的状态同步 使用了第三方protobuf库作为报文和序列化,关于如何在windows下安装参考:windows下安装golang pr ...

  2. 计算机网络各层级协议报文形式详解

    网络层各级协议报文样式 概述 协议 所属层次 单位 长度 点对点协议 PPP 数据链路层 Mac帧 64~1518 IP协议 网络层 分组 packet 20~65535 UDP 运输层 数据报 8~ ...

  3. railgun:通过代码来简单说明

    在说明之前我先展示一下我的工程目录层级,因为我连github有点慢,所以像protoc.exe这样上了1M的文件就不传了.所以如果从github上获取源码的同学可能还需要再去我的百度云盘上获取buil ...

  4. modbus报文解析实例_万字长文!春招面试总结,鹅厂T3Android高频面试真题+解析...

    前言: 大家好呀,我是你们的卑微小凯.春招已经接近尾声啦,各位跳槽找工作的小伙伴有没有收获自己满意的offer呢. 小编最近收纳整合了各一线互联网大厂Android岗的面试真题,由初级到进阶,每题都有 ...

  5. 【计算机网络】HTTP 与 HTTPS ( HTTP 在网络各个层级的传输过程 | HTTPS 工作流程 | HTTPS 弊端 )

    文章目录 一.HTTP 在网络各个层级的传输过程 二.HTTPS 工作流程 1.中间人篡改服务器下发的数字证书 2.中间人冒充服务器端 三.HTTPS 弊端 一.HTTP 在网络各个层级的传输过程 应 ...

  6. Java读取多层级xml文件

    最近在做国际客服北京职场的项目,需要提供一个接口服务端的能力,也就是需要开发一个http+xml的协议,入参和出参均为Map格式,各系统间的请求或应答是以xml格式封装的.在将返回报文(xml)解析为 ...

  7. 计算机网络 数据段、报文、IP数据报、数据包、MAC帧的区别;应用层、运输层、网络层、数据链路层、物理层的区别与功能;转发器、集线器、网桥、交换机、路由器、网关的功能与区别

    主要解决三个问题: 1.数据段.报文.IP数据报.数据包.MAC帧的区别 2.应用层.运输层.网络层.数据链路层.物理层的区别与功能 3.转发器.集线器.网桥.交换机.路由器.网关的功能与区别 数据传 ...

  8. python scapy抓取http报文内容

    一.使用scapy,简单的用来抓取http相关报文 #coding=utf-8import scapy.all as scapy from scapy.layers.http import HTTPR ...

  9. Java解析XML报文内容及标签属性

    前言 JSON格式的报文解析,虽然json串短小精悍,也能有效表达层次结构,但是每个元素只能找到对应的元素值,不能体现更丰富的样式特征.比如某个元素除了要传输它的字符串文本,还想传输该文本的类型.字体 ...

最新文章

  1. linux ntfs 挂载 centos,centos linux ntfs iso 挂载
  2. 用python画四叶草代码-python—字符串拼接三种方法
  3. 选择存储服务器硬盘并解决一些疑问
  4. nginx负载均衡集群
  5. 抓取豆瓣上的《长津湖》的热评,我发现了这些
  6. linux打包运行python文件_Linux下安装pyinstaller用于将py文件打包生成一个可执行文件...
  7. 肿瘤动物模型中需要注意的伦理问题
  8. 微信小程序实现商品数量加减案例
  9. 创建Django项目及配置
  10. 模糊C均值聚类算法(原理+Matlab代码)
  11. 解决Chrome 的右键谷歌网页翻译失效 20221107更新
  12. 红米note4x装linux,红米Note4X自己安装Magisk的过程
  13. 我们的时空之旅(一)——丽江古城
  14. Python爬虫实战爬取租房网站2w+数据-链家上海区域信息(超详细)
  15. 为你的树莓派WI-FI配置加密密码
  16. 利用python和Sen2cor对Sentinel2进行批量大气校正
  17. # Windows下关于安装Geany编辑器过程中的一点小发现(可能对初次安装的人有用)
  18. 《Going Deeper with Convolutions》阅读笔记
  19. 苹果手机怎么恢复丢失的数据?果粉必看!
  20. [转载]Mac地址、IP、子网掩码、网关、DNS(转)

热门文章

  1. 洗碗机超声波换能器振子设计
  2. 一文告诉你什么是财务数据治理?
  3. python二分法查找程序_基础算法——查找(二分法)(python)
  4. Shell整理笔记(一)
  5. Angular 入门(二)
  6. 从梁飞的微型rpc 细节说起--Dubbo源码系列解读(5)
  7. 漫画 | 老板,我想申请加薪~
  8. 深度学习笔记(三):BatchNorm(BN)层
  9. 现代计算机控制理论及应用王万良 pdf,计算机}j现代化.PDF
  10. jsp与servlet的关系