原帖地址:http://gad.qq.com/article/detail/7171195
原文作者:唐声福 原帖备注:版权所有,禁止匿名转载;禁止商业使用;禁止个人使用。
1、概述
1.1、基于UDP的帧同步方案
在技术选型方面,之所以选择帧同步方案,在Kevin的一篇介绍PVP帧同步后台实现的文章中已经做了详细叙述,这里简单摘要如下:
高一致性。如果每一帧的输入都同步了,在同样的上下文中,计算得出的结果应该也是同步的。
低流量消耗。除了帧同步,其它方案(比如状态同步)想做到高一致性,需要同步非常大量的数据。无论是对于移动网络,还是固络都是不合适的。
服务器逻辑简化。采用帧同步方案,服务器只需要做简单的帧同步,不需要关心太多的业务细节。有利于客户端功能的扩展和服务器的稳定和性能。
反作弊。客户端只需要在适当机时上报校验数据给服务器,服务器对2个客户端上报的数据进行对比,就可以快速识别是否有人作弊。然后通过无收益的方式间接防止作弊。
那么,为什么选择UDP而不是TCP呢?主要有2点原因:
弱网络环境。
实时性要求。
我们通过一个测试APP,在WIFI和4G环境下,采用TCP和UDP两种方式连接同一个服务器,分别获得对应的RTT进行对比。
我们可以发现,在弱网络环境下,UDP的RTT几乎不受影响。而TCP的RTT波动比较大,特别是受丢包率影响比较明显。
1.2、基于UDP的FSP协议栈
由于UDP具有不可靠性,所以在UDP的基础上实现一个自定义的协议栈:FSP,即FrameSyncProtocol。
FSP的基本原理就是防照TCP的ACK/SEQ重传机制,实现了传输的可靠性,同时还采用冗余换速度的方式,又保证了传输的**速率。在帧同步方案中一举两得。
2、技术原理
2.1、帧同步技术原理
如下图所示,客户端A的操作A1与客户端B的操作B1封装成OperateCmd数据发送给PVP服务器。PVP服务器每66MS产生一个逻辑帧,在该帧所在时间段内收到A1和B1后,生成一个Frame数据块,在该帧时间结束时,将Frame发送给客户端A和B。Frame数据块内有该帧的帧号。客户端A和B收到Frame数据后,便知道该帧内,客户端A和B都做了什么操作。然后根据收到的操作A1和B1进行游戏表现,最终呈现给玩家A和B的结果是一致的。从而实现客户端A与B的数据同步。
图1 帧同步技术原理
2.2、FSP协议栈原理
如下图所示,发送者维持一个发送队列,对每一次发送进行编号。每一次发送时,会将待发送的数据写入队列。然后将队列里的数据+编号发送给接收者。
接收者收到数据后,会将该编号回送给发送者以确认。发送者收到确认编号后,会将该编号对应的数据包从队列中删除,否则该数据仍保存在发送队列中。
下次发送时,会有新的数据进入队列。然后将队列中的数据+最新的编号发送给接收者。以此循环反复。
图2 FSP协议栈原理
上图解析:
第1次发送,在发送队列里只有Data1,于是将Data1和编号1(Seq=1)发送给接收者。收到确认编号1(Ack=1)后,将Data1从队列中删除。
第4到7次发送,由于从第4次发送开始就没有收到确认编号,于是队列中包含了Data4到Data7。第7次发送后,收到确认编号6,于是将Data4至Data6从队列中删除。
第8次发送,队列中包含Data7和Data8。发送后收到确认编号8,从而将Data7和Data8从队列中删除。
以上的关键点是,发送者未收到确认编号,并不一直等待,而是会继续下一次发送。结合图1:
如果发送者是服务器,则会每隔66MS会将一个Frame数据写入发送队列,然后将该队列里的所有Frame数据一起发送给客户端 。
如果发送者是客户端,则会在玩家有操作时,将玩家的每一个OperateCmd数据写入发送队列,然后将该队列里的所有OperateCmd数据一起发送给服务器 。如果发送队列不为空,则每隔99MS重复发送。如果发送队列为空,则不再发送。直到玩家下一次操作。
由于服务器和客户端即是发送者,又是接收者。则服务器和客户端的每一次发送,除了会带上该次发送的编号,还会带上对对方发送编号的确认 。
3、技术实现
3.1、整体框架
图3 PVP通讯模块整体框架
这是一个典型的手游PVP通讯模块的整体框架。这里主要分享一下FSP模块和帧同步模块的技术实现。
3.2、FSP模块
FSP模块主要用来实现FSP协议栈。其协议格式定义如下。
FSP上行协议定义:
Seq
Ack
SomeData
OperateCmd List
CheckSum
FSP下行协议定义:
Seq
Ack
Frame List
CheckSum
如下图所示,是FSP模块的接收逻辑流程。
图4 FSP模块接收逻辑流程
其中关键点是:
对Recv New Ack判断,对曾经发送过的Operate进行确认删除。
对Recv New Seq判断,过滤掉因为网络问题造成乱序的包。
上图中,接收到的Frame最终都存储在RecvQueue中。我们将接收逻辑放在子线程中。所以只需要在主线程中需要Recv的时刻从RecvQueue中读取FremeList即可。
如下图所示,是FSP模块的发送逻辑流程。发送逻辑同样放在子线程中。发送逻辑有2种触发方式:
业务层主动调用发送
每隔指定时间触发一次(在WIFI和4G下使用不同的时间,可以减少服务器收到的纯确认包比例,有利于提高通讯性能)
图5 FSP模块主动发送逻辑流程
图6 FSP模块定时发送逻辑流程
3.3、帧同步模块
下图是帧同步模块的实现框架。
图7 帧同步模块实现框架
按照上图箭头编号描述如下:
(1)负责接收来自FSP模块的FrameList。
(2)将FrameList里的每1帧都存入FrameQueue。
(3)同时将FrameList的每1帧的帧号进行变换后,得到客户端帧号。同时,在等下1个服务器帧到来之前,需要将客户端的帧锁定在下1个服务器帧的前一帧(LockFrameIndex)。然后 将FrameIndex和LockFrameIndex传入FrameBuffer。
(4)客户端每1帧从FrameBuffer中取出当前可能需要跳帧加速的倍数(SpeedUpTimes)。
(5)如果SpeedUpTimes为0,则表示正在缓冲中,没有需要处理的帧。如果SpeedUpTimes是1,则表示缓冲结束,但是不需要加速,只需要处理最新的1帧。如果SpeedUpTimes大于1,则从FrameQueue里取出这SpeedUpTimes个帧, 将里面的SyncCmd取出来。
(6)将SyncCmd传入OperationExecutor。
(7)OperationExecutor与具体游戏的业务逻辑相关联,负责将SyncCmd传入给业务逻辑和预表现模块进行具体的处理。
其流程图如下:
图8 帧同步逻辑流程1
图9 帧同步逻辑流程2
4、最新优化
4.1、断线重连优化
在传统网络模块开发思想中,当发送超时达到阀值,或者底层判定断开连接时,需要重新建立连接。之前这部分工作是交给一个偏上层的模块来执行,该模块需要等Apollo通讯模块连接成功之后,才进行PVP通讯模块的连接。这样使逻辑变得复杂。
由于UDP本身的不可靠性,可以认为网络断线也是其不可靠性的一部分。
而FSP协议栈就是为了解决UDP的不可靠性而设计的,所以也附带解决了断线重连问题。
去除了原来的断线重连逻辑之后,用FSP模块本身的特性来处理断线重连,实测能够提高网络恢复的响应速度。由于PVP服务器设定的超时阀值是15秒,有些时候,其实网络已经恢复,但是由于Apollo通讯模块对网络的恢复响应过于迟钝,造成不必要的判输。
4.2、接入GSDK
从目前接入GSDK后的数据来看,能够减少一定的网络延时,但是并不明显。
4.3、AckOnly优化
AckOnly优化是指减少服务器收到的纯确认包数据。这样做的目的是:
减少包量,有助于在WIFI下节省路由器性能。GSDK有个统计表明,有大概20%多的网络延时是因为路由器性能造成。
节省流量,一定程度上也可以节省网络设备性能,同时在4G下为用户省钱。
该优化分2部分实现:
(1)空帧免确认
(2)WIFI延迟确认
在优化前的AckOnly比例为:57%
空帧免确认优化后降到:38%
WIFI延迟确认优化后降到:25%
5、一些尝试
将FSP模块抽象得与业务无关,使之可快速完成一个使用帧同步方案通讯的Demo成为可能。
实验了本地局域网PVP对局,只要在同一网段下,可以成功对局。(如果有需求,可以实现该功能)
实验了本地蓝牙PVP对局,发现蓝牙是带连接态的,并且其通讯是用类似TCP的数据流进行的。同时它与WIFI信号有干扰,如果开启WIFI,其延时非常高。在非WIFI下,其单条数据的延时很低,但是如果以66MS的频率发送数据,则延时又非常高。
建立了一套用于FSP在线诊断和断线诊断的工具。
致谢
感谢Gates让我重构PVP通讯模块,以及Kevin帮忙统计数据。
- 动作手游实时PVP帧同步方案(客户端)
1.概述 1.1.基于UDP的帧同步方案 在技术选型方面,之所以选择帧同步方案,在Kevin的一篇介绍PVP帧同步后台实现的文章中已经做了详细叙述,这里简单摘要如下: 高一致性.如果每一帧的输入都同步 ...
- 动作手游实时PVP技术揭密(服务器篇)
前言 我们的游戏是一款以忍者格斗为题材的ACT游戏,其主打的玩法是PVE推图及PVP 竞技.在剧情模式中,高度还原剧情再次使不少玩家泪目.而竞技场的乐趣,伴随着赛季和各种赛事相继而来,也深受玩家喜爱, ...
- 手游后台PVP系统网络同步方案总结
游戏程序 平台类型: 程序设计: 编程语言: 引擎/SDK: 概述 PVP系统俨然成为现在新手游的上线标配,手游Pvp系统体验是否优秀,很大程度上决定了游戏的品质.从最近半年上线的新手 ...
- 腾讯首度公开S级手游品质管理方法
weimjsam 引言 在最新的手游市场占有率统计中,腾讯游戏稳稳占据一半江山,目前仍以每月一到两款的速度推出新品,在如此复杂多变.响应要求极高的市场环境下,能持续推出高质量产品并保持高效迭代更新 ...
- Unity手游引擎安全解析及实践
近日,由Unity主办的"Unity技术开放日"在广州成功举办,网易移动安全技术专家卓辉作为特邀嘉宾同现场400名游戏开发者分享了网易在手游安全所积累的经验.当下,很多手游背后都存 ...
- 一款已上市MMO手游地图同步方案总结
1. 客户端地图格子的相关知识 在2.5D的MMO游戏里,角色是通过3D的方式渲染,2D的地图是通过2D的方式显示,所以在客户端一般会有三个坐标系: a) 3D坐标系:所有需要3D渲染的角色和光效,都 ...
- 【转】一款已上市MMO手游地图同步方案总结
转自游戏开发主席 1. 客户端地图格子的相关知识 在2.5D的MMO游戏里,角色是通过3D的方式渲染,2D的地图是通过2D的方式显示,所以在客户端一般会有三个坐标系: a) 3D坐标系:所有需要3D渲 ...
- 如何让手游内存占用更小?从内存消耗iOS实时统计开始
为什么iOS内存使用过多会崩溃,性能会下降?腾讯游戏学院专家Devlin在本文给了解释,如何让手游内存占用更小?从内存消耗iOS实时统计开始. 一.问题 在之前的手游项目中,内存使用过多,都开始崩溃了 ...
- EasyPusher实现Android手机屏幕桌面直播,实时推送操作画面,用于手游直播等应用
本文转自EasyDarwin开源团队成员John的博客:http://blog.csdn.net/jyt0551/article/details/52651194 由于Android 5.0提供了捕获 ...
随机推荐
- java stopwatch 功能
C#中有一个stopwatch的功能,主要是用来监测程序执行时间的.java之前一直都在用如下方式完成: public static void main(String[] args) { long s ...
- 每天一个linux命令(49):at命令
在windows系统中,windows提供了计划任务这一功能,在控制面板 -> 性能与维护 -> 任务计划, 它的功能就是安排自动运行的任务. 通过'添加任务计划'的一步步引导,则可建立一 ...
- iOS-提高iOS开发效率的方法和工具
提高iOS开发效率的方法和工具 介绍 这篇文章主要是介绍一下我在iOS开发中使用到的一些可以提升开发效率的方法和工具. IDE 首先要说的肯定是IDE了,说到IDE,Xcode不能跑,当然你也可能同时 ...
- Win10+VS2015+EF6.0+MySQL5.6+MVC环境部署和排错
本来已经好几个月不弄专业了,今天一哥们问我问题的时候重新捣鼓了一下,就做个文档供后来人参考吧~ 1.首先安装MySQL 5.6 安装很简单我就不说了,你参考下:http://jingyan.baidu ...
- 详解jquery插件中;(function ( $, window, document, undefined )的作用
在jquery插件中我们经常看到以下这段代码 1 2 3 ;(function ( $, window, document, undefined ){ //函数体内具体代码 })(jQuery, wi ...
- 深入理解DOM节点类型第七篇——文档节点DOCUMENT
× 目录 [1]特征 [2]快捷访问 [3]文档写入 前面的话 文档节点document,隶属于表示浏览器的window对象,它表示网页页面,又被称为根节点.本文将详细介绍文档节点document的内 ...
- JAVA服务器搭建之问题总结
负责维护公司产品的web服务器搭建与维护,最近遇到一下状况,今天在这里简单总结一下,希望对于刚刚一些刚入行的小伙伴有所帮助,避免再走弯路. 第一点:Tomcat内存设置: 一.常见的Java内存溢出有 ...
- 【记录】AutoMapper Project To OrderBy Skip Take 正确写法
AutoMapper:Queryable Extensions 示例代码: using (var context = new orderEntities()) { return context.Ord ...
- AOP的实现机制--转
原文地址:http://www.iteye.com/topic/1116696 1 AOP各种的实现 AOP就是面向切面编程,我们可以从几个层面来实现AOP. 在编译器修改源代码,在运行期字节码加载前 ...
- 附录C 编译安装Hive
如果需要直接安装Hive,可以跳过编译步骤,从Hive的官网下载编译好的安装包,下载地址为http://hive.apache.org/downloads.html . C.1 编译Hive C.1 ...