游戏网络框架设计
2023-12-02 04:24:30 10 举报
游戏网络框架设计
作者其他创作
大纲/内容
socket
至此会发现前端的协议代码和后端的协议代码以及用于序列化和反序列化的类都是一致的,所以一般在开发中都是使用工具生成的这一前后端一致的代码,protobuf就是这样的一个开源工具,当然也可以自己制作一个协议生成工具,可以参考:https://www.processon.com/view/6567b01d7e49a75d71a20497
Socket
这个流程的最终结果是服务器收到了客户端这边一么一样的byte数组,那么接下来关心的是服务器这边如何将这个byte数组转成客户端一致的PlayerInfo对象了,这个时候就需要客户端和服务器端双方制定数据的序列化和反序列化协议了
UDP是无连接的,我们如何记录连入的客户端?
流转byte[]
客户端
WriteData() : 反序列化方法
Byte是在程序之中的最低存储单位,那么为了实现刚开始提出的将PlayerInfo对象传输给后端,那么现在得出了两种方式,将PlayerInfo对象转成byte数组,再将byte数组通过Socket传输给服务器端,具体怎样传输不用太过关心,现在只需要关系的是,在客户端PlayerInfo对象转byte数组之后经过网络传输,最终服务器以流或者数据报的方式收到,再将其转成byte即可。具体如下:
经过与后端商议,我们决定将id保存在前4位,将name在bytes中的长度保存在5-8位,之后的都是name数据,有了这个我们就可以在后端将前4位转int并赋值给PlayerInfo的id,将5-(8+name在bytes中的长度)的数据装string,并赋值给PlayerInfo的name。
WriteInt() : 操作byte数组,往里面写入一个int类型
如下图所示,客户端和服务器端都提前知道数据应该如何处理,根据前4位的type和5-8位的id即可确定后面的数据应该使用什么类类型来进行处理,在前后端中这个协议是以代码的形式存在的,一个消息对应一个类型,每个类型都保存了type id 和所需要用于序列化和反序列化的数据传输类,那么数据传输类是怎样的一个存在呢?数据传输类应该有一个bytes数组,有往容器中写各种数据的API,有序列化和反序列化的功能
这里我们可以让客户端每相隔10秒向服务器端发送一条没有数据,只有头部信息的包,这个叫做心跳包,服务器端每次收到这个包都把客户端id和接收到心跳包的时间保存起来,同时在服务器开始的时候就开启一个线程每相隔5秒就遍历一次这个容器,将时间超过10秒的服务器中保存的clientSocket从服务器的列表中移除,只有在服务器中有clientSocket的客户端才算是在线状态,当然可以按照这种思路给客户端加入更多的状态
客户端保存的协议信息
那么RPC又是什么?RPC即是远程调用,在后端网络框架中比较常见,我们口中的RPC即使RPC框架,用于两个终端之间的消息通讯的框架
type:1, id:1,data: xxx
id
如何解决粘包和分包问题?
服务器
ReadData(Byte byte) : 序列化方法
服务器端保存的协议信息
ReadInt() : 从byte数组中读取一个int类型的数据
UDP
这里先提一个问题,这里说的UDP是无序的不可靠的,那么为什么还要用来作为游戏网络框架的开发底层呢?
byte[]
5-(8+name在bytes中的长度)的数据装string,并赋值给PlayerInfo的name。
......
用于序列化和反序列化的数据传输类(前后端一致)
byte数组
我们一般说的采用UDP的游戏都是先建立一次TCP的连接的,建立了TCP连接之后在进行UDP通讯,所以准确来说是TCP+UDP通讯,这样服务器端就可以获取到clientSocket对象并保存到列表中了
RPC
读取前4位位int,赋值给PlayerInfo的id
name在bytes中的长度
那么他们之间是什么的关系呢?简单来说TCP、UDP是一套数据传输规则,Socket是基于TCP和UDP技术实现的一套工具,而RPC又是基于Scoket基础是的更进一步的功能封装
这里以流+TCP实现的Socket通讯为例子,服务器端收到客户端连接之后会获取到一个clientSocket对象,这个对象保存了客户端信息,包括端口号和ip,我们可以把这个对象存在一个容器中,需要广播的时候遍历整个容器进行逐一的消息发送,如果需要单独给某一个客户端发送消息,那么我们可以给每个客户端的socket打上id,并保存在kv容器中(还可以分类型存储不同端的客户端socket对象)
.......
这里不是包含关系,意思是Socket这套工具使用了TCP或者UDP协议,而RPC又是在Socket基础上进行的封装,最终到手的是一个RPC框架,这个框架对外提供API来实现通讯
消息类型(int)
消息id(int而且唯一)
真正传输的数据
什么是Socket?Socket中文叫套接字,可以理解为实现网络通讯的一个工具,可以依赖TCP或者UDP的方式来使用常用的组合方式有 流套接字(流 + TCP),主要用于实现TCP的通讯,数据报套接字(数据报 + UDP),主要用于UDP通讯,提供无连接的通讯服务,但是数据包有大小限制
粘包和分包在TCP中是都存在的,UDP中只存在分包,粘包和分包都是系统和网络收发机制决定的,这个我们是没有办法干预的。我们能做的只要有在应用层自己写逻辑来解决,对收到的消息进行长度判断,然后手动拆分和拼接成原来客户端应用层发送的样子,UDP的分包首先就需要自己定义规则解决UDP的顺序问题,没有顺序会阻碍UDP数据的重新拼接,当然也可以通过设置数据不超过,MTU来解决这个问题,如果数据没有超过MTU是不会被拆分的。
name的数据
转成流
至此数据的收发功能已经完成了,那么,服务器端是何如确认客户端的状态的呢(如何判断客户端是否离线)?
至此整个流程已经OK,不知道你有没有发现,整个流程中服务器端都是知道客户端要发送的是PlayerInfo对象,而且服务器端从一开始就知道前四位是int类型的id,5-8位是int类型的string长度信息,后面的数据是string类型的name。这个服务器端提前知道的信息就叫做协议。现在来说的话是只有一条协议,所以前后端就算口头商议好都可以实现数据的传输,要是有更多类型的数据需要传输呢,比如Monster、心跳信息等等?这个该如何应对呢?前后端一个一个hardcode明显是不现实的,所以我们需要做的工作是将消息归类。如何归类?举个例子:这里我们向头部添加一些数据来确定具体前后端发送和接受到的是具体那个消息
至此数据的收发功能已经完成了,那么,服务器端是何如向客户端广播消息的呢?如何单独给某个客户端发送消息的呢?
这里不具体写实现了,具体可以参考ByteBuffer,只要继承ByteBuffer并且增强功能对外提供API即可
TCP
0 条评论
下一页