摘要: 本文做为游戏服务器端开发的基本大纲,是游戏实践开发中的总结。第一部分专业基础,用于指导招聘和实习考核, 第二部分游戏入门,讲述游戏服务器端开发的基本要点,第三部分服务端架构,介绍架构设计中的一些基本原则。但愿能帮到你们
一 专业基础
1.1 网络
-
1.1.1 理解TCP/IP协议
-
网络传输模型
-
滑动窗口技术
-
创建链接的三次握手与断开链接的四次握手
-
链接创建与断开过程当中的各类状态
-
TCP/IP协议的传输效率
-
思考
-
-
1)请解释DOS攻击与DRDOS攻击的基本原理
-
2)一个100Byte数据包,精简到50Byte, 其传输效率提升了50%
-
3)TIMEWAIT状态怎么解释?
-
1.1.2 掌握经常使用的网络通讯模型
-
Select
-
Epoll,边缘触发与平台出发点区别与应用
-
Select与Epoll的区别及应用
-
1.2 存储
-
计算机系统存储体系
-
程序运行时的内存结构
-
计算机文件系统,页表结构
-
内存池与对象池的实现原理,应用场景与区别
-
关系数据库MySQL的使用
-
共享内存
-
1.3 程序
-
对C/C++语言有较深的理解
-
深入理解接口,封装与多态,而且有实践经验
-
深入理解经常使用的数据结构:数组,链表,二叉树,哈希表
-
熟悉经常使用的算法及相关复杂度:冒泡排序,快速排序
二 游戏开发入门
-
2.1防护式编程
-
不要相信客户端数据,必定要检验。做为服务器端你没法肯定你的客户端是谁,你也不能假定它是善意的,请作好自我保护。(这是判断一个服务器端程序员是否入门的基本标准)
-
务必对于函数的传人参数和返回值进行合法性判断,内部子系统,功能模块之间不要太过信任,要求低耦合,高内聚
-
插件式的模块设计,模块功能的健壮性应该是内建的,尽可能减小模块间耦合
-
2.2 设计模式
-
道法天然。不要迷信,迷恋设计模式,更不要生搬硬套
-
简化,简化,再简化,用最简单的办法解决问题
-
借大宝一句话:设计本天成,妙手偶得之
-
2.3 网络模型
-
自造轮子: Select, Epoll, Epoll必定比Select高效吗?
-
开源框架: Libevent, libev, ACE
-
2.4 数据持久化
-
自定义文件存储,如《梦幻西游》
-
关系数据库: MySQL
-
NO-SQL数据库: MongoDB
选择存储系统要考虑到因素:稳定性,性能,可扩展性linux
-
2.5 内存管理
-
使用内存池和对象池,禁止运行期间动态分配内存
-
对于输入输出的指针参数,严格检查,宁滥勿缺
-
写内存保护。使用带内存保护的函数(strncpy, memcpy, snprintf, vsnprintf等),严防数组下标越界
-
防止读内存溢出,确保字符串以’\0’结束
-
2.6 日志系统
-
简单高效,大量日志操做不该该影响程序性能
-
稳定,作到服务器崩溃是日志不丢失
-
完备,玩家关键操做必定要记日志,理想的状况是经过日志能重建任什么时候刻的玩家数据
-
开关,开发日志的要加级别开关控制
-
2.7 通讯协议
-
采用PDL(Protocol Design Language), 如Protobuf,能够同时生成先后端代码,减小先后端协议联调成本, 扩展性好
-
JSON,文本协议,简单,自解释,无联调成本,扩展性好,也很方便进行包过滤以及写日志
-
自定义二进制协议,精简,有高效的传输性能,彻底可控,几乎无扩展性
-
2.8 全局惟一Key(GUID)
-
为合服作准备
-
方便追踪道具,装备流向
-
每一个角色,装备,道具都应对应有全局惟一Key
-
2.9 多线程与同步
-
消息队列进行同步化处理
-
2.10 状态机
-
强化角色的状态
-
前置状态的检查校验
-
2.11 数据包操做
-
合并, 同一帧内的数据包进行合并,减小IO操做次数
-
单副本, 用一个包尽可能只保存一份,减小内存复制次数
-
AOI同步中减小中间过程无用数据包
-
2.12 状态监控
-
随时监控服务器内部状态
-
内存池,对象池使用状况
-
帧处理时间
-
网络IO
-
包处理性能
-
各类业务逻辑的处理次数
-
2.13 包频率控制
-
基于每一个玩家每条协议的包频率控制,瘫痪变速齿轮
-
2.14 开关控制
-
每一个模块都有开关,能够紧急关闭任何出问题的功能模块
2.15 反外挂反做弊
-
包频率控制能够消灭变速齿轮
-
包id自增校验,能够消灭WPE
-
包校验码能够消灭包拦截篡改
-
图形识别吗,能够踢掉99%非人的操做
-
魔高一尺,道高一丈
-
2.16 热更新
-
核心配置逻辑的热更新,如防沉迷系统,包频率控制,开关控制等
-
代码基本热更新,如Erlang,Lua等
-
2.17 防刷
-
关键系统资源(如元宝,精力值,道具,装备等)的产出记日志
-
资源的产出和消耗尽可能依赖两个或以上的独立条件的检测
-
严格检查各项操做的前置条件
-
校验参数合法性
-
2.18 防崩溃
-
系统底层与具体业务逻辑无关,能够用大量的机器人压力测试暴露各类bug,确保稳定
-
业务逻辑建议使用脚本
-
系统性的保证游戏不会崩溃
-
2.19 性能优化
-
IO操做异步化
-
IO操做合并缓写 (事务性的提交db操做,包合并,文件日志缓写)
-
Cache机制
-
减小竞态条件 (避免频繁进出切换,尽可能减小锁定使用,多线程不必定因为单线程) 多线程不必定比单线程快
-
减小内存复制
-
本身测试,用数听说话,别猜
-
2.20 运营支持
-
接口支持:实时查询,控制指令,数据监控,客服处理等
-
实现考虑提供Http接口
-
2.21 容灾与故障预案
-
略
三 服务器端架构
-
3.1 什么是好的架构?
-
知足业务要求
-
能迅速的实现策划需求,响应需求变动
-
系统级的稳定性保障
-
简化开发。将复杂性控制在架构底层,下降对开发人员的技术要求,逻辑开发不依赖于开发人员自己强大的技术实力,提升开发效率
-
完善的运营支撑体系
-
3.2 架构实践的思考
-
简单,知足需求的架构就是好架构
-
设计性能,抓住重要的20%, 不必从程序代码里面去抠性能
-
热更新是必须的
-
人不免会犯错,尽量的用一套机制去保障逻辑的健壮性
游戏服务器的设计是一项很有挑战性的工做,游戏服务器的发展也由之前的单服结构转变为多服机构,甚至出现了bigworld引擎的分布式解决方案,最近了解到Unreal的服务器解决方案atlas也是基于集群的方式。程序员
负载均衡是一个很复杂的课题,这里暂不谈bigworld和atlas的这类服务器的设计,更多的是基于功能和场景划分服务器结构。算法
首先说一下思路,服务器划分基于如下原则:数据库
- 分离游戏中占用系统资源(cpu,内存,IO等)较多的功能,独立成服务器。
- 在同一服务器架构下的不一样游戏,应尽量的复用某些服务器(进程级别的复用)。
- 以多线程并发的编程方式适应多核处理器。
- 宁肯在服务器之间多复制数据,也要保持清晰的数据流向。
- 主要按照场景划分进程,若需按功能划分,必须保持整个逻辑足够的简单,并知足以上1,2点。
各个服务器的简要说明:编程
Gateway 是应用网关,主要用于保持和client的链接,该服务器须要2种IO,对client采用高并发链接,低吞吐量的网络模型,如IOCP等,对服务器采用高吞吐量链接,如阻塞或异步IO。后端
网关主要有如下用途:设计模式
- 分担了网络IO资源
- 同时,也分担了网络消息包的加解密,压缩解压等cpu密集的操做。
- 隔离了client和内部服务器组,对client来讲,它只须要知道网关的相关信息便可(ip和port)。
- client因为一直和网关保持常链接,因此切换场景服务器等操做对client来讲是透明的。
- 维护玩家登陆状态。
World Server 是一个控制中心,它负责把各类计算资源分布到各个服务器,它具备如下职责:api
- 管理和维护多个Scene Server。
- 管理和维护多个功能服务器,主要是同步数据到功能服务器。
- 复杂转发其余服务器和Gateway之间的数据。
- 实现其余须要跨场景的功能,如组队,聊天,帮派等。
Phys Server 主要用于玩家移动,碰撞等检测。数组
全部玩家的移动类操做都在该服务器上作检查,因此该服务器自己具有全部地图的地形等相关信息。具体检查过程是这样的:首先,Worldserver 收到一个移动信息,WorldServer收到后向Phys Server请求检查,Phys Server检查成功后再返回给world Server,而后world server传递给相应的Scene Server。安全
Scene Server 场景服务器,按场景划分,每一个服务器负责的场景应该是能够配置的。理想状况下是能够动态调节的。
ItemMgr Server 物品管理服务器,负责全部物品的生产过程。在该服务器上存储一个物品掉落数据库,服务器初始化的时候载入到内存。任何须要产生物品的服务器均与该服务器直接通讯。
AIServer 又一个功能服务器,负责管理全部NPC的AI。AI服务器一般有2个输入,一个是Scene Server发送过来的玩家相关操做信息,另外一个时钟Timer驱动,在这个设计中,对其余服务器来讲,AIServer就是一个拥有不少个NPC的客户 端。AIserver须要同步全部与AI相关的数据,包括不少玩家数据。因为AIServer的Timer驱动特性,可在很大程度上使用TBB程序库来发 挥多核的性能。
把网络游戏服务器分拆成多个进程,分开部署。这种设计的好处是模块天然分离,能够单独设计。分担负荷,能够提升整个系统的承载能力。
缺点在于,网络环境并不那么可靠。跨进程通信有必定的不可预知性。服务器间通信每每难以架设调试环境,并很容易把事情搅成一团糨糊。并且正确高效的管理多链接,对程序员来讲也是一项挑战。
前些年,我也曾写过好几篇与之相关的设计。这几天在思考一个问题:若是咱们要作一个底层通用模块,让后续开发更为方便。到底要解决怎样的需求。这个需求应该是单一且基础的,每一个应用都须要的。
正如 TCP 协议解决了互联网上稳定可靠的点对点数据流通信同样。游戏世界实际须要的是一个稳定可靠的在游戏系统内的点对点通信须要。
咱们能够在一条 TCP 链接之上作到这一点。一旦实现,能够给游戏服务的开发带来极大的方便。
能够把游戏系统内的各项服务,包括并不限于登录,拍卖,战斗场景,数据服务,等等独立服务当作网络上的若干终端。每一个玩家也能够是一个独立终端。它们一块儿构成一个网络。在这个网络之上,终端之间能够进行可靠的链接和通信。
实现能够是这样的:每一个虚拟终端都在游戏虚拟网络(Game Network)上有一个惟一地址 (Game Network Address , GNA) 。这个地址能够预先设定,也能够动态分配。每一个终端均可以经过游戏网络的若干接入点 ( GNAP ) 经过惟一一条 TCP 链接接入网络。接入过程须要经过鉴权。
鉴权过程依赖内部的安全机制,能够包括密码证书,或是特别的接入点区分。(例如,玩家接入网络就须要特定的接入点,这个接入点接入的终端都必定是玩家)
鉴权经过后,网络为终端分配一个固定的游戏域名。例如,玩家进入会分配到 player.12345 这样的域名,数据库接入可能分配到 database 。
游戏网络默认提供一个域名查询服务(这个服务能够经过鉴权的过程注册到网络中),让每一个终端都能经过域名查询到对应的地址。
而后,游戏网络里全部合法接入的终端均可以经过其地址相互发起链接并通信了。整个协议创建在 TCP 协议之上,工做于惟一的这个 TCP 链接上。和直接使用 TCP 链接不一样。游戏网络中每一个终端之间相互发起链接都是可靠的。不只玩家能够向某个服务发起链接,反过来也是能够的。玩家之间的直接链接也是可行的(是否容许 这样,取决于具体设计)。
因为每一个虚拟链接都是创建在单一的 TCP 链接之上。因此减小了互连网上发起 TCP 链接的各类不可靠性。鉴权过程也是一次性惟一的。而且咱们提供域名反查服务,咱们的游戏服务能够清楚且安全的知道链接过来的是谁。
系统能够设计为,游戏网络上每一个终端离网,域名服务将广播这条消息,通知全部人。这种广播服务在互联网上难以作到,但不管是广播仍是组播,在这个虚拟游戏网络中都是可行的。
在这种设计上。在逻辑层面,咱们可让玩家直接把聊天信息从玩家客互端发送到聊天服务器,而不须要创建多余的 TCP 链接,也不须要对转发处理聊天消息作多余的处理。聊天服务器能够独立的存在于游戏网络。也可让广播服务主动向玩家推送消息,由服务器向玩家发起链接,而 不是全部链接请求都是由玩家客互端发起。
虚拟游戏网络的构成是一个独立的层次,彻底能够撇开具体游戏逻辑来实现,并可以单独去按承载量考虑具体设计方案。很是利于剥离出具体游戏项目来开发并优化。
最终,咱们或许须要的一套 C 库,用于游戏网络内的通信。api 能够和 socket api 相似。额外多两条接入与离开游戏网络便可。