一、前言
网络游戏所面临的挑战:
- 一致性:如何在所有的主机内都保持一样的表现
- 可靠性:网络传输有可能出现丢包
- 安全性:反作弊,反信息泄漏。
- 多样性:不同设备之间链接,比如手机,ipad,电脑等设备,热更新逻辑。
- 复杂性:高并发,高效率,高表现。
二、网络协议
Vint Cerf和Robert Kahn设计了TCP/IP协议用于在不同主机之间传递信息。
2.1 OSI七层网络
需要详细了解,可以去我的计算机网络专栏查看。
2.2 Socket
通过socket接口就可以和别的主机建立链接。
2.3 TCP与UDP
这里详细说了,去我计算机传输层看吧。
2.4 Reliable UDP
网络游戏:基于UDP的可靠网络连接。
TCP传输可靠但是太慢了,UDP很快,但是他不保证可靠传输,很容易丢包。所以我们需要在UDP上加上确认机制和重传机制等方法来保证数据的可靠传输。
2.5 FEC
在传输时传输足够数量的额外冗余信息,在一定程度上重建丢失的数据。
1. XOR-FEC
通过异或运算求解丢失的数据。但是这个适用于丢一个包的数据,当丢多个相邻包的数据时可以用下面这个方法。
2.Reed-Solomon codes
三、时钟同步与RPC
在游戏开始前,需要对准所有客户端的游戏时间。
3.1 RTT
往返传输时延
3.2 NTP
与服务器对时间
NTP算法:
3.3 Remote Procedure call(RPC)
程序员只需要重点关注逻辑,不需要关注那么多的网络方面的。
通过在服务端定义各种方法,客户端只需要调用和传递参数即可:
四、网络拓扑
4.1 P2P
4.2 Dedicatd Server
五、游戏同步
5.1 快照同步(Snapshot Synchronization)
类似云游戏,客户端只做输入和显示,别的都交给服务器。
优化:
- 服务端按照10fps计算,客户端按照60fps显示,多的fps进行插值。
- 数据压缩,只发送变化的数据。
这样导致客户端的算力浪费了,并且对带宽要求很高。
5.2 帧同步(LockStep Synchronization)
类似军队中每个人步调一致的向前走,在相同的时间点,各个客户端的表现一致。
同样的输入 + 同样的处理逻辑 = 同样的表现
例如游戏中的回放并不是游戏战斗过程的录屏,而是在游戏时记录所有玩家的输入,然后将这些输入重新计算就可以得到同样的效果。
第一款使用帧同步的游戏:Doom。王者荣耀也是用帧同步做的。
帧同步的过程:每一帧所有的客户端把你输入交给服务器,服务器汇总后再发给每个客户端,每个客户端按照同样的逻辑处理这些输入。
优点:非常简单,特别公平
缺点:最慢的那个客户端会拖慢所有的客户端。
优化:不等最慢的客户端,设定一个deadline,超出deadline后就不管没有提交的输入了,而是发放已经提交的输入。
帧同步需要让所有客户端保持一致性 :
-
浮点数 :所有的数学运算都要符合原理。2/3在所有客户端的结果都是一样的。
-
随机数:每个客户端生成的随机数也是一样的,使用随机种子。所以计算机生成的随机数是伪随机
-
存储所有的数据,当游戏出现差错时可以回溯
-
使用buffer来解决延时的问题,
-
在逻辑帧中添加渲染帧,避免抖动。
-
断线重连,将别人的数据存储在本地,当玩家回来后根据这些逻辑追帧。
帧同步实现反作弊面临的问题:
- 一个客户端修改时数据后,服务端根据别的大多数客户端的数据判断,然后剔除作弊的客户端。
- 如果只有两个客户端的话,服务器就不能判断谁在作弊了。
- 帧同步是把所有客户端的数据都传到一个客户端进行计算,所以对于单个客户端来说很容易做一个插件让玩家知道所有人当前的状态。
优点:带宽要求小,只需要同步所有的指令。对于即时性的游戏很合适。
缺点:保持一致性面临很多挑战,难以避免全图挂。
5.3 状态同步(State Synchronization)
每个客户端提交自己的输入信息,server端会根据所有客户端输入模拟整个世界,然后将对应玩家的结果状态返回给对应玩家。注意并不是所有的状态,这里要和快照同步区分。
玩家A的客户端状态玩家A说了算,但是在别的客户端玩家A的状态是由服务器说了算。在玩家B的客户端看到的玩家A只是玩家A的复制品,他的行为逻辑是由服务器计算反馈的。
注意哦,所有玩家对场景还是别的玩家造成的影响,无论在哪个客户端,结果都是由服务端说的算。
存在的问题:dump client problem,由于客户端的表现要受到服务端的控制,所以玩家输入后由于网络问题可能会看到延时反应的效果。
解决方法:
1.Client-side Prediction
2.Server Reconciliation