Games104——网络游戏的架构基础
这里写目录标题
- 多人网络游戏面临的挑战
- 网络协议
- OSI模型
- Socket
- TCP/IP协议
- UDP协议
- 基于UDP的可靠连接
- 自动重复的要求
- Forward Error Correction FEC
- XOR-FEC(异或运算)
- Reed-Solomon Codes
- 时钟同步&RPC
- RTT
- NTP
- RPC
- Stubs
- 网络拓扑
- 快照同步
- 帧同步
- Initialization
- Lag and Delay
- 断线重连
- 反作弊
- 状态同步
- 客户端预测
- 服务端和解
- 丢包问题
- 状态同步和帧同步的区别
多人网络游戏面临的挑战
- 保证每个用户事件的一致性
- 网络延迟和丢包
- 反作弊反信息泄露
- 复杂的设备,快速进行热更新迭代
- 复杂度高
网络协议
OSI模型
Socket
通过一个简单的接口与另外一台机器建立连接就可以持续不断的传输数据
TCP/IP协议
特点:连接稳定、流量控制、包头长,保证信息的一致性
TCP的拥塞控制
对于对时间不太敏感但对稳定性有要求的游戏适合用TCP
UDP协议
端到端的协议
特点:不需要建立长时间的链接、不需要流量控制、包头轻、传播很快、但稳定性不行容易传达不到
在不稳定的网络情况下要求响应速度块适合用UDP,如吃鸡类
基于UDP的可靠连接
对于大型MMO不会使用单一协议,哪个适合用那个,游戏开发很多时候不会使用原生态协议
自动重复的要求
- Stop and Wait ARQ
等待ACK包到达后再去发下一个
- Go Back N ARQ
发现一个包丢失后将窗口中的重新传一遍
- 选择重传ARQ
告诉哪个包没有传到,重传没传到的那个包即可
- Forward Error Correction FEC
Forward Error Correction FEC
XOR-FEC(异或运算)
如果丢失任何数据包,可以使用其他四个数据包进行恢复。
连续数据中只能丢失一个数据包。如果A和B同时丢失,算法将无法恢复。
不适用于丢包率高的情况
Reed-Solomon Codes
可以通过矩阵计算得到丢掉的数据
丢包率高的情况(比如移动端)也可以保证传输数据
可以根据具体需要DIY传输协议
时钟同步&RPC
对战游戏的重点就是要将时钟对准
RTT
我发一个包给对方多久能得到一个回包
RTT是自己写的,Ping更偏向底层
如何去对时间?
NTP
默认上行下行的时间是对等的,但事实上上下两路不一定是对等的,所以该算法不会很准确
在波动的网络下无法对准时间,只能推测
客户端和服务端建立连接时第一时间就要把钟对好
- 把NTP算法先跑一遍,算出来客户端时间和服务端时间的差别
- 调整客户端的时钟,因为网络会波动,所以多做几次NTP算法,记录每次RTT
- 去掉RTT比平均值高的值,再做一次平均,再用这个平均值对钟进行调整
RPC
对程序员而言在服务器和客户端之间网络传输会有以下的困难:
运行用不同语言编写
过程使用不同大小的数据类型
使用不同的字节顺序(字节序)
以不同的方式表示浮点数
具有不同的数据对齐要求
RPC让程序员不需要定义复杂的网络报体结构,从而专注的写游戏逻辑,只需要传几个参数即可
Stubs
相当于票据存根
Stubs Compiler读取 lDL 声明,并生成两个 stub
服务器程序员实现服务的程序,并将其与服务器端的stub链接起来。
客户端程序员实现客户端程序并将其与客户端的stubs链接起来
subs管理客户端和服务器之间远程通信的所有细节。
网络拓扑
P2P
早期使用的网络架构,研发商不需要维护服务器
Dedicated Server
在服务器端维护一个游戏世界,由研发商提供
允许不同地区的玩家在服务器之间建立高速的连接
快照同步
早期使用
客户端把自己的输入发给服务器,服务器把整个游戏世界的状态生成一个快照,然后把快照发给所有的客户端
保证了状态的一致性
服务端和客户端之间的帧率不同,快照生成的算力消耗大,所以使用Delta
缺点:快照数据量大,不适合用户数据量大的情况
客户端没有进行计算,浪费了客户端的算力
帧同步
服务器:信息的同步汇总转发
Initialization
初始化游戏内核心数据,保证局内数据高度一致化
服务器获取到所有客户端的输入,并将所有客户端的输入再发放给每个客户端,但最慢的用户会拖慢所有的进程
设置一个上限,如果有客户端在时间内没有输入则直接舍弃,但会产生一些潜在的bug和用户输入无效的问题
难点:要构建一个确定性的游戏世界
如何解决
- 保证浮点数的高度一致性
- 保证随机数的确定性
Lag and Delay
无法规避网络抖动就可以使用buffer
在各个节点catch很多的数据,在本地网络抖动时smooth,以达成观看时稳定的效果
解决网络延迟和抖动的问题
将逻辑帧和渲染帧分割开来,画面不会抖动
断线重连
断线重连时可以根据客户端留存的快照减少断线重连时产生的差距,方便追上
服务端会在几个关键帧进行快照,当玩家过长时间断线时可以根据服务端的快照,帮助setup游戏的状态
另外一个重要的应用场景是观战
基于帧同步的机制可以拓展出这些游戏玩法
反作弊
投票机制
每隔一段时间每个客户发送一个校验码,如果有一个客户的校验码有误则将其踢掉
帧同步的特点是在客户端可以获取到局内所有的状态,游玩时使用插件就可以获取到这些状态,但现在的游戏会采取一些方法来规避这些问题
优点:可以做一些打击状态清晰的对抗,方便做游戏录屏
缺点:保持一致性很难
状态同步
同步用户的状态信息,如关键性的爆炸,死亡等
每一个玩家提供部分的信息,Server去模拟一个世界,只把与每个用户相同的信息发给他,防作弊的能力好于帧同步
- Authorized
玩家本地客户端 - Replicated
在其他玩家客户端上1P的复制体
整个世界的核心逻辑都是由Server计算的
不要求所有的客户端保证确定性
状态同步面临的问题
客户端预测
先预测半个RTT的时间再加上一个Command frame的动作,等服务端数据回来之后再对齐,尽可能让移动平滑
服务端和解
如果在Server的时间点状态不一致,那必须根据Server的要求反向矫正行为
服务端需要一个RingBuffer存储过去几帧,出现了状态不一致的问题后RingBuffer之后的数据全部无效化重新算一遍
丢包问题
用RingBuffer把输入存储下来,在获取不到输入时采用最后一次输入的状态
状态同步适合网络和游戏业务比较复杂的情况