08 网络同步
计算机网络基础
计算机网络体系结构
Socket
- 通信流程:Socket是“打开―读/写一关闭”模式的实现,UDP模式服务器端没有Listen、
Accept,客户端没有Connect流程。
TCP&UDP
P2P连接
- P2P又称点对点,是无中心服务器、依靠用户群( peers)交换信息的互联网体系。对等网络的每个用户端既是一个节点,也有服务器的功能。
C/S架构
- C/s架构也称主从式架构,架构分为客户端和服务器,所有客户端只和服务器对连,客户端之间的通信必须通过服务器。软件开发简单,可控性好。但是服务器性能压力大。
数据同步基础
RPC(Remote Procedure Call)
RPC,即远程过程调用。本地调用远端提供的函数方法,因为不是一个内存空间,不能直接调用,需要通过网络来表达调用的语义和传达调用的数据。
RPC调用带来的问题
- 在远程调用时,我们需要执行的函数体是在远程的机器上的。也就是执行是在另一个进程中。另一端是不知道函数指针和堆栈数据的。这就带来了几个新问题:
- Call ID映射
- 序列化和反序列化
- 网络传输
- 在远程调用时,我们需要执行的函数体是在远程的机器上的。也就是执行是在另一个进程中。另一端是不知道函数指针和堆栈数据的。这就带来了几个新问题:
对象序列化
像函数ID映射一样,本地用到对象实例想要在另一端也能够直接使用,这样会大大简化上层业务逻辑开发,这就需要对象序列化。
对象序列化是将对象转换成一系列字节,这样对象就可以很容易地保存到持久存储中,或者通过通信链路传输。然后将字节流反序列化――转换为原始对象的副本。
属性同步
- 属性同步是对象序列化带来的额外福利,对象A的某个属性更改了,其他端的镜像A对象对应的属性也需要更改,这就是属性同步。属性同步一般是服务器更改,同步到其他所有客户端,主要是为了解决数据冲突和安全性考虑
网络同步
- 所谓同步,就是要多个客户端表现效果一致
帧同步
- 帧同步技术是早期RTS游戏常用的一种同步技术。帧同步只同步操作,其大部分游戏逻辑都在客户端上实现,服务器主要负责广播和验证操作,有着逻辑直观易实现、数据量少、可重播,一致性好等优点。
状态同步
状态同步同步的是游戏中的各种状态。
一般的流程是客户端上传操作到服务器,服务器收到后计算游戏行为的结果,然后以广播的方式下发游戏中各种状态,客户端收到状态后再根据状态显示内容。
状态同步其实是一种不严谨的同步。不同玩家屏幕上的表现的一致性并不是重要指标,只要每次操作的结果相同即可。所以状态同步对网络延迟的要求并不高。像玩RPG游戏,200-300ms的延迟也可以接受。但是在RTS游戏中,50ms的延迟也会很受伤。
对比
状态同步 | 帧同步 | |
---|---|---|
客户端一致性 | 最终一致,中间可能拉扯 | 一直保持一致 |
网络延迟要求 | 低 | 高 |
网络流量 | 与物体数量成正比 | 与操作数量成正比 |
服务器任务 | 接受输入、全局仿真、状态复制 | 同步操作 |
客户端任务 | 局部游戏世界的展示 | 全局仿真 |
玩家断线或中途加入 | 容易 | 困难,如需从第一帧重新运算 |
开发效率 | 低,前后台联合开发 | 高,接近于单机游戏 |
安全性 | 服务器逻辑安全性高,部分功能在客户端实现较容易作弊 | 安全性低,但可事后验证 |
网络延迟和抖动
网络延迟高或者抖动,会影响游戏体验,常见的表现有拉扯,瞬移,攻击敌方没有伤害等
对抗网络延迟的方法:
- 平滑算法
- 插值
- 预测
- 缓存
- 延迟补偿
- 平滑算法
UE网络同步
Unreal网络架构
以“客户端-服务器”模型为基础
实现面向对象式封装
实现网络代码和游戏逻辑完全分离
网络同步支持可视化编程
网络协议使用UDP通讯
Unreal为何使用纯UDP协议?
网络模式类型 | 功能/理想用例 |
---|---|
NM_Standalone | 此类型说明服务器在本地计算机上运行且不接受来自远程计算机的客户端。此类型最适合单人游戏或本地多人游戏。 |
NM_DedicatedServer | 专属服务器没有本地玩家,可以通过丢弃声音、图形、用户输入和其他面向玩家的功能来更高效地运行。此类型服务器用于托管在受信任服务器上的多人游戏,如竞赛性MOBA游戏、MMO游戏或需要高性能可靠服务器的在线射击游戏。 |
NM_ListenServer | 监听服务器是一个既托管本地玩家,同时也对来自远程玩家的连接开放的服务器。此类型服务器适合于不需要专属服务器,且用户可以在没有第三方服务器的情况下设置和玩自己的游戏的竞争或合作类游戏。由于主机没有网络延迟,此类型网络模式可能会给主机带来一些优势,并且可以在没有主机警告的情况下终止游戏。 |
NM_Client | 这是唯一为非服务器的模式。在此模式下,本地计算机是专属或监听服务器的客户端,因此不会运行服务器端逻辑。 |
Actor的复制
Actor是UE网络同步核心。所有的数据同步都是围绕Actor展开的。服务器将保留一份Actor列表并定期更新客户端,以便客户端保留每个Actor(那些需要被同步的Actor)的近似复本。
Actor主要通过两种方式进行更新:
- Replicate属性更新
- RPC(远程过程调用)
- 可以在蓝图或者在通过C++代码设置Actor可以被复制。组件也可以进行类似的设置
属性复制的规则
只支持从服务器复制到客户端,且都是Reliable的
在每帧更新完所有Actor的属性状态会执行一遍所有Actor的属性复制
只复制那些与上一帧对比发生过变化的属性
如果是第一次复制,则复制跟默认值对比不一致的属性
只支持复制静态数组,并且只复制数组中与上一帧对比发生过变化的那一项
支持复制结构体,结构体中任意属性与上一帧对比发生过变化,都会导致复制结构体的所有属性
属性复制支持回调事件
RPC
- 支持服务器和客户端双向复制
- 可以被标记为:Client、Server、 NetMulticast·可以是Reliable或者UnReliable的
- 函数复制是没有返回值的,也不支持输出型参数
RPC要求和注意事项
- 它们必须从Actor上调用。
- 如果RPC是从服务器调用并在客户端上执行,则只有实际拥有这个Actor的客户端才会执行函数。
- 如果RPC是从客户端调用并在服务器上执行,客户端就必须拥有调用RPC的Actor。
- 多播RPC则是个例外:
- 如果它们是从服务器调用,服务器将在本地和所有已连接的客户端上执行它们。
- 如果它们是从客户端调用,则只在本地而非服务器上执行。
- 现在,我们有了一个简单的多播事件限制机制:在特定Actor的网络更新期内,多播函数将不会复制两次以上。按长期计划,我们会对此进行改善,同时更好的支持跨通道流量管理与限制。
UE网络的实现
NetDriver ——网络管理类
- 管理网络连接,驱动整个网络收发逻辑
- 创建和管理NetConnection
- 收发网络Packet
- 除了GameNetDriver,还有DemoNetDriver专门处理录像回放的逻辑
NetConnection ——抽象的网络连接 & Packet——数据包
- 客户端与服务器之间的连接
- 服务器会有多个NetConnection,每一个对应一个客户端连接
- 客户端会有一个ServerConnection
- 创建和管理Channel
- 每个通信包是一个Packet,每个Packet有一个唯一的Seq,Seq自增且不会重复利用
- Packet 网络不稳定可能出现丢包,丢包时Connection不会重发,但会通知Channel
Channel——数据传输的管道
- 每个NetConnection管理着自己的Channel集合
- Channel以Bunch为单位进行数据的接收和发送
- 发送时,Channel将Bunch数据交给NetConnection以Packet发送
- 接收时,NetConnetion将Bunch分发给对应的Channel进行处理。
- Channel有4种类型
- ActorChannel
- ControlChannel
- FileChannel
- VoiceChannel
ActorChannel
- 实现数据传输的面向对象式的封装
- 每个ActorChannel绑定一个与玩家相关的Actor
- 每个ActorChannel只传输绑定的Actor所需要同步的数据
Bunch——管道中传输的数据串
数据流通的基本单位
位流,充分利用了每一个Bit
- 对于bool型的数据,使用一个Bit来存储
- 对于其他类型的数据,会根据数据的范围选择最节省的若干个Bit来存储
Reliable(可靠) & UnReliable(不可靠)
- Reliable Bunch,保证一定会发送到远端,并且按发送的顺序进行处理
- UnReliable Bunch,网络丢包有可能导致Bunch不能发送到远端