1、通信
1.1、TCP vs UDP
TCP,保证数据可靠性是有代价的.发送一个数据包,等待一段时间,如果没有收到它的ack,就重新发送丢失的数据包到目标计算机;即使最新的数据包已经到达目标机器,还需要等待丢失的包重新发过来之后,才能访问;并且重复的数据包将被丢弃在接收端,乱序的数据包将被重新排序.
UDP,不能保证数据包的顺序,无法保证不丢包.为此,优化的UDP协议可以客服上述问题:
1.为每个数据包增加序号,每发一次包,增加本地序号
2.每个数据包增加增加一个位域,用来容纳多个确认符,发包速率越高,确认字符数量也越多
3.每收到一个包,把包上的序列号,变为确认字符,发包的时候带上这些确认字符
4.如从确认字符中,发现有数据包丢失,把它留给应用程序来包含丢失数据的新的数据包,请求重发
5.针对多次收到同一个包的时候,丢弃处理
1.2、KCP
KCP是一个快速可靠协议,能以比 TCP浪费10%-20%的带宽的代价,换取平均延迟降低 30%-40%,且最大延迟降低三倍的传输效果。纯算法实现,并不负责底层协议(如UDP)的收发,需要使用者自己定义下层数据包的发送方式,以 callback的方式提供给 KCP。 连时钟都需要外部传递进来,内部不会有任何一次系统调用。本文传输协议只考虑UDP的情况。 名词说明(源码字段):
用户数据:应用层发送的数据,如一张图片2Kb的数据
MTU:最大传输单元。即每次发送的最大数据
RTO:Retransmission TimeOut,重传超时时间。
cwnd:congestion window,拥塞窗口,表示发送方可发送多少个KCP数据包。与接收方窗口有关,与网络状况(拥塞控制)有关,与发送窗口大小有关。
rwnd:receiver window,接收方窗口大小,表示接收方还可接收多少个KCP数据包
snd_queue:待发送KCP数据包队列
snd_nxt:下一个即将发送的kcp数据包序列号
snd_una:下一个待确认的序列号
2、同步
2.1 帧同步
一般mmorpg,采用的是状态同步,如魔兽,所有的状态由服务器来控制,安全性比较高,但是流量比较大;帧同步采用囚徒模式,所有的客户端强制采用同一个逻辑帧率,保证输出一致,特点是流量小,安全性较差.
2.2 帧率
在服务端,是指一次逻辑循环的频率,如30帧/秒
2.3 LockStep
确保连接到同一服务器的同一“房间”/“战斗地图”的玩家,步调一致;这是因为帧同步主要依靠客户端计算,来提高更好的战斗/竞技体验(对防外挂要求很高)。如下图所示,可以保证多个客户端同步进行。
图中是A、B、C三个玩家的时间轴,这个时间轴不是电脑上的本地时间,而是A、B、C联机时定义的一个时间轴。虚线分隔出来时间片称为turn(服务端,逻辑帧),可以理解成一帧。箭头表示该玩家将自己的操作指令广播给其他玩家。我们把一盘游戏看成一个大型的状态机,因为大家玩的是同一款的游戏,因此F是相同的,初始状态S0也是相同的。在第一个turn结束时,所有玩家都接收到了完全一样的输入I,注意这里的I不是一个值,而是包含了当前游戏中所有玩家的操作指令集合。t1时刻所有玩家的电脑自行计算结果。由于F、S0和I是固定的,所以每个玩家电脑上计算出的下一个状态S1一定是相同的。
2.4 囚徒模式
2.4.1、我们把游戏的前进分为一帧帧,这里的帧和游戏的渲染帧率并不是一个,只是借鉴了帧的概念,自定义的帧,我们称为turn。游戏的过程就是每一个turn不断向前推进,每一个玩家的turn推进速度一致。
2.4.2、每一帧只有当服务器集齐了所有玩家的操作指令,也就是输入确定了之后,才可以进行计算,进入下一个turn,否则就要等待最慢的玩家。之后再广播给所有的玩家。如此才能保证帧一致。
2.4.3、Lockstep的游戏是严格按照turn向前推进的,如果有人延迟比较高,其他玩家必须等待该玩家跟上之后再继续计算,不存在某个玩家领先或落后其他玩家若干个turn的情况。使用Lockstep同步机制的游戏中,每个玩家的延迟都等于延迟最高的那个人。
2.4. 4、由于大家的turn一致,以及输入固定,所以每一步所有客户端的计算结果都一致的。
2.5 乐观锁与断线重连
囚徒模式的帧同步,有一个致命的缺陷就是,若联网的玩家有一个网速慢了,势必会影响其他玩家的体验,因为服务器要等待所有输入达到之后再同步到所有的c端。另外如果中途有人掉线了,游戏就会无法继续或者掉线玩家无法重连,因为在严格的帧同步的情况下,中途加入游戏是从技术上来讲是非常困难的。因为你重新进来之后,你的初始状态和大家不一致,而且你的状态信息都是丢失状态的,比如,你的等级,随机种子,角色的属性信息等。 一旦掉线基本这局就废了,需要重开,至于为何没有卡顿的现象,因为那时都是解决方案都是采用局域网的方式,所以基本是没有延迟问题的。
后期为了解决这个问题,如今包括王者荣耀,服务器会保存玩家当场游戏的游戏指令以及状态信息,在玩家断线重连的时候,能够恢复到断线前的状态。不过这个还是无法解决帧同步的问题,因为严格的帧同步,是要等到所有玩家都输入之后,再去通知广播client更新,如果A服务器一直没有输入同步过来,大家是要等着的,那么如何解决这个问题?
采用“定时不等待”的乐观方式在每次Interval时钟发生时固定将操作广播给所有用户,不依赖具体每个玩家是否有操作更新。如此帧率的时钟在由服务器控制,当客户端有操作的时候及时的发送服务器,然后服务端每秒钟20-50次向所有客户端发送更新消息。如下图:
2.6 技能同步
游戏中有很多是和概率相关的,比如说技能的伤害有一定概率的暴击伤害或者折光被击等。按照帧同步的话,基于相同的输入,每个玩家的client都是独立计算伤害的,那么如何保证所有电脑的暴击伤害一致那。这个时候就需要用到伪随机了。 大部分编程语言内置库里的随机数都是利用线性同余发生器产生的,如果不指定随机种子(Random Seed),默认以当前系统时间戳作为随机种子。一旦指定了随机种子,那么产生的随机数序列就是确定的。就是说两台电脑采用相同的随机种子,第N次随机的结果是一致的。
所以在游戏开始前,服务器为每个玩家分配一个随机种子,然后同步给client,如此每个client在计算每个角色的技能时候,就能保证伤害是一致的。这也是多数帧同步游戏采用的方案,包括王者荣耀。
服务器架构简单把服务器按功能做了如下划分
类型
序号
服务器
说明
通用
1
登录服务器(Login Server)
认证(或完成第三方认证),需要注意大量玩家涌入的情况(重要的技术指标:QPS,TPS也即系统吞吐量)
2
Gate服
承载玩家的连接,路由消息等;路由效率,延时等
3
聊天服(Chat)
有的游戏,甚至做了比较专业的聊天功能,相应的聊天服务器也会比较复杂些
核心
1
游戏服(GameLogic)
可集群,分摊玩家的养成等功能
2
PVP匹配服(PVPServer)
匹配玩家,如分地区匹配等,让少数玩家进入一个房间服
3房间服(Room)
玩家对战的服务器,帧同步。
4大厅服
玩家登录后,随即进入大厅。承载量是主要技术指标。需要维持数量相当高的在线人数;可以考虑水平/分区扩展的方式来承载。
1、简单的服务器架构
本例只有简单的登陆,房间,匹配,聊天等服务器,相对简单
2、实战服务器架构
将不同的功能,做了划分,做成了分布式的架构,并增加了网关服等通用服务器。
版权申明:本站文章均来自网络,如有侵权,请联系01056159998 邮箱:itboby@foxmail.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有