游戏中的网络模块

大纲

  • 网络模块
  • Socket
  • 游戏中的网络框架:框架设计、TDR/ProtoBuf
  • 解决方案:传输协议、弱网处理、网络同步
  • 问题解答

目的

  • 游戏项目中网络相关开发知识

网络模块

游戏中的网络模块与其它软件系统基本原理并无差异,基本上是解决如何把网络消息快速安全的发送到其端,然后其他端及时地处理该消息并作出对应的游戏表现。

RPG同步问题
背包系统的网络流程

网络模型

  • OSI 开放系统互连模型(Open System Interconnect)
OSI
OSI
OSI
  • TCP/IP 协议栈分层模型
TCP/IP堆栈
TCP/IP

游戏开发中最主要会使用到应用层和传输层,在传输层中TCP与UDP之间的区别在于:

  • TCP:传输可靠(保证顺序),三次握手建立连接,数据包没有限制,不丢包(重传机制RDT)有拥塞控制,可以依靠网络层去分帧。
  • UDP:传输不可靠(不保证顺序可能丢包),强调传输性而非可靠性,实现机制比较简单,无连接随时发送且包小于64K。

关于TCP与UDP的选择,TCP相比UDP数据安全可靠,但需要建立连接,机制复杂带来额外的开销,在网络环境不好的情况下效果非常差。简单来说,TCP适合应用在网络条件较好且对安全可靠性有要求的场景,UDP适合应用在网络环境较差且要求响应速度比较高(安全性是其次的)。

在网络游戏开发中,通信协议无论对于客户端开发还是服务器开发都很重要,因此制定一个合适的通信协议是很有必要的。例如,在有联网需求的弱网游戏如即时互动类游戏中,选择通信协议主要会关注延迟低、易用性、低成本等因素。

下面简单对比下TCP、UDP、HTTP、WebSocket四种协议的利弊和特性:

TCP

  • 特性:全双工协议、面向连接且保证可靠性、基于IP层对应OSI参考模型中位于传输层、适用于二进制传输
  • 优点:可靠性、全双工协议、开源支持多、应用较广泛、面向连接、研发成本低、报文内容无限制(IP层自动分包、重传、不大于1452字节)
  • 缺点:操作系统方面比较消耗内存且支持的连接数量有限,设计上看协议比较复杂且自定义应用层协议,网络方面看在网络差的情况下延迟较高,传输方面来看效率低于UDP协议。

UDP

  • 特性:无连接不可靠,基于IP协议层对应OSI参考模型中的传输层,最大努力交付,适用于二进制传输。
  • 优点:操作系统层面看并发高且内存消耗较低,传输层面看效率高且网络延迟低,另外传输模型简单且研发成本低。
  • 缺点:协议不可靠且是单向协议,开源支持少,报文内容有限不能大于1464字节,设计上协议比较复杂,在网络差的环境中容易丢失数据报文。

HTTP

  • 特性:基于TCP/IP的应用层协议、无状态无连接、支持C/S模型、适用于文本传输
  • 优点:协议比较成熟,基于TCP/IP且拥有TCP优点,研发成本很低,开源软件较多(Apache、Nginx、Tomcat...)
  • 缺点:无状态无连接,只有PULL模式且不支持PUSH,数据报文较大。

WebSocket

  • 特性:有状态、面向连接、数据报头小、适用于Web3.0以及即时联网通讯。
  • 优点:协议比较成熟,基于TCP/IP且拥有TCP的优点,数据报文较小且包头非常小,面向连接而且是有状态的协议,开源较多开发较快。
  • 缺点:暂无

对比特性后如何选择协议呢?

  • 对于弱联网类游戏如消除类、卡牌类可以直接使用HTTP,若考虑安全性可使用HTTPS或是对内容提做对称加密。
  • 对于实时性且交互性要求较高的可优先选择WebSocket其次是TCP。
  • 对于实时性要求极高且可达性要求一般的可选择UDP
  • 局域网对战类、赛车类可直接使用UDP,不过对于公网对战、P2P的UDP还需要进行“打洞”处理。

Socket

在同一台机器上两个不同的进程之间有多种通信方式,如管道、消息队列、信号量、信号、共享内存等。网络中要在两个不同机器的进程之间通信,就需要Socket套接字。从概念上来看,Socket是一套基于TCP/IP协议封装的API,它处于网络应用层给开发者提供方便的接口来快速实现网络通信。从编程角度来讲,Socket是一个无符号整形变量,用来标识一个通信进程。两个进程在网络中通信就必须要知道对方的IP和端口以及通信所采用的协议栈,Socket和它们绑定。

在网络中传递一个对象时,客户端上的对象与服务器上的对象在内存地址上肯定是不一样的,如何知道客户端传递过来的对象就是服务器上的呢?GUID,服务器在同步一个对象引用(指针)的时候,会为其分配专门的GUID并通过网络进行发送。客户端上通过识别这个ID就可以找到对应的类对象。

Socket位置

Socekt是应用层与TCP/IP的中间软件抽象层,是一组接口。在设计模式中Socket是一个门面模式,它把复杂的TPC/IP隐藏在Socket接口后面。对用户来说就是一组简单的接口,使用Socket去组织数据以符合指定的协议。

Socket操作流程

服务器首先初始化Socket,然后与端口绑定,对端口及逆行监听,调用accept阻塞,等待客户端连接。此时如果有客户端初始化一个Socket,然后连接服务器,如果连接成功此时客户端与服务器端的连接就建立了。客户端发送数据请求,服务器接收请求并处理请求,然后把回应数据发送给客户端,客户端读取数据,最后关闭连接,一次交互结束。

Socket中发送的TCP三次握手

Socket中TCP的三次握手建立连接,即交换三个分组。

  1. 客户端向服务器发送一个SNY J
  2. 服务器向客户端响应一个SYN K,并对SYN J进行确认ACK J+1。
  3. 客户端在向服务器发一个确认ACK K+1

当客户端调用connect时触发了连接请求,先服务器发送SYN J包,此时connect进行堵塞状态。服务器监听到连接请求即收到SYN J包,调用accept函数接收请求向客户端发送SYN K、ACK J+1。此时accept进入阻塞状态。客户端收到服务器的SYN K,ACK J+1之后此时connect返回并对SYN K进行确认。服务器收到ACK K+1时accept返回,至此三次握手完毕建立连接。

简单来说,客户端的connect在三次握手的第二次返回,而服务器端的accept在三次握手的第三次返回。

Socket的工作方式

对于Socket最需要关注的是它的工作方式,在客户端Socket主要有两种工作方式:

  • 同步方式

无论是TCP还是UDP,在使用Socket进行连接、发送、接收的时候,在未完成工作前代码不再继续往下执行,处于等待状态,知道语句完成对应工作后才继续执行下一条语句。

值得注意的是,TCP和UDP对于一件工作是否完成的定义不同,以发送Send为例:

对于TCP而言,未完成的工作包括:

  • 缓冲区满了数据无法写入
  • 数据写入了但还没轮到它发送
  • 数据发送了但还未收到ACK确认

对于UDP而言,未完成的工作就是缓冲区满了就数据无法写入了。

Socket同步方式发送时未完成工作的时序图
  • 异步方式

异步方式下不论对应工作是否完成都会往下执行,但工作完成后,是通过一个回调函数来通知调用者的。注意这个回调时在一个Socket内部创建的子线程上下文。

在同步方式中,可以理解为有一个Loop不停地轮询是否完成了工作,直到工作完成后才结束Loop。为了避免UI以及主逻辑被卡住,一般需要将以同步方式工作的操作都放到自制的子线程中。而在异步方式中,实质上是Socket内部创建了一个子线程。

游戏中的网络架构

理解了Socket之后所谓游戏中的网络框架也不难以理解,其实就是在Socket基础上进一步封装一套更方便游戏内部的消息传输机制,同时将消息解析细节和逻辑层代码进行分离,使逻辑层代码更加清晰可读。

框架设计要点

  • 独立分层设计
  • 事件机制
  • 单独线程编码解码
游戏中网络框架设计

游戏中常用的模块之间通信时,使用事件与代理可以极大的降低模块耦合,实现原理是函数指针,比如A模块执行了某个操作后通过广播向所有模块发送消息,这些模块如果事先绑定了对应消息的函数指针,就会收到该消息并处理。这个过程中A并不知道发送给谁,也不知道其他模块又做了什么。

低配版网络框架设计:比如客户端想要发送一个比较复杂的数据结构,如一个包含字符串和数字的结构体。首先这个数据需要在客户端转换成二进制,通过Socket发送到服务器,服务器的网络机制通过其Socket监听该数据包,然后解析二进制数据并还原到逻辑上层。

高配版网络框架设计:游戏中希望直接能将一个对象从客户端发送到服务器,或者直接将某个函数发送到服务器去执行,更甚者想要在逻辑层实现UDP的可靠数据传输,这些较为复杂的机制都包含在网络框架中,可借用第三方开源库实现如Google的Protobuf。

ProtoBuf是一种轻便高效的结构化数据存储格式,与平台和语言无关且可扩展,通常用于通讯协议和数据存储等领域。简单来说或,就是用来按照二进制格式保存和读取的开源库,在进行网络传输的时候需要把数据转换成二进制,通过网络层发送过去,但是如何把复杂数据(类对象)准确地转换成二进制发送并在接收端快速准确解析,这就是一个问题。ProtoBuf就可以做这个工作,它可以把一个对象序列化成二进制,然后在接收端再反序列化陈原来的对象内容,这样就成功的传输了一个类对象。这个过程是在应用层实现,所以本质上游戏网络层的实现对应的就是网络模型中应用层。

游戏开发协议设计

协议是通信双方能够理解的一种数据格式,是为网络中进行数据交换而建立的规则、标准或约定的集合。协议设计主要包含三要素:

  • 语法:用户数据与控制信息的结构与格式,以及数据出现的顺序。
  • 语义:解释控制信息每个部分的意义,规定了需要发出何种控制信息,以及完成的动作与做出什么样的响应。
  • 时序:对事件发生顺序的详细说明
    简单来说,语义是表示要做什么,语法表示要怎么做,时序表示做的顺序。

由于游戏的特殊性,对流量消耗要尽量的少,安全性要求更高,以及对平台支持要足够多等。着就要求游戏协议设计需要尽量简单、通用,代码层面上易扩展、解析效率足够高等特点。

基于此,我们需要从三个层次来考虑游戏设计的方案:

  • 应用层面:文本协议、二进制协议、数据格式
  • 安全层面:常规加密、动态加密
  • 传输层面

应用层面常用的协议类型包括文本类型和二进制类型

  • 文本协议

文本协议设计的目的是方便人理解,典型的HTTP协议格式就非常贴近文字描述。由于HTTP是客户端浏览器与Web服务器之间的应用层通信协议,所以适用性非常广泛。不过你会发现,当需要基于一个很简单的应答时,HTTP同样需要带上很多头信息,这对于流量有要求的游戏应用来说,是非常浪费的。

文本协议的优点在于通用且适用性广泛、方便理解且可读性好。而缺点在于它是基于行读的因此解析效率一般,另外HTTP携带的附带信息过多而造成传输效率低下,由于HTTP是无状态的服务器也就不知道客户端的状态,因此必须基于客户端的请求来回应,如此一来实时性就很低。另外,文本协议很难嵌入其他数据,且对二进制支持很差。

如果对游戏实时性要求不高且对流量要求也不高的情况,文本协议是一个不错的选择,一般而言短链接游戏多使用这种方式。

  • 二进制协议

二进制协议就是一串字节流,是一个典型的IP协议,通常包括消息头和变长的消息体,消息头的长度固定,消息体的长度不固定,消息体包含着主要的内容主体。一般消息头都会包含消息体的长度,这样就能够基于头信息从数据流中解析出完整的二进制消息。

二进制协议head消息头部分包含

  • cmd 命令字
    二进制协议中命令字是双方协议文档中规定好的,比如0x01表示登录、0x02表示注册等,其实就是一个命令号。
  • sign 验证串
    验证串是一个验证字符串,对消息体数据进行一定加密验证,保证数据安全。
  • content-leg 消息体长度
  • HeaderCRC 非必须的头验证

二进制协议消息体body部分包含

message:login{
string username;
int64 password;
}

因为字段的数据类型有定义,顺序也有定义,整个二进制流读取的时候,是基于顺序读取的。

二进制协议的优点在于没有冗余字段,因此传输高效且耗费流量小,其次由于是基于基础数据类型操作所以解析速度快。缺点在于可读性差且不利于调试,其次可扩展性差,对于复杂数据结构支持不够友好。

如果游戏对实时性要求比较高且对流量也有要求,使用二进制协议会比较好,一般大型多人网游都采用二进制协议来设计。

数据交换格式

一般而言消息体的格式决定了语义和时序,格式不同的数据的序列化和反序列化也是不同的,比如message:login中可以基于JSON格式来定义,也可以基于XML格式来定义,定义不同解析方式也各不相同。一般的消息体数据格式主要包括:JSON、ProtoBuf、XML、自定义等等。

数据交换格式对比

JSON

JSON是一种轻量级的数据交换格式,互联网应用广泛,框架支持很多。其优势在于开源且格式统一,解析速度一般。缺点在于会有一些冗余字节不够简洁。

ProtoBuf

ProtoBuf是Google提供的开源序列化框架,类似XML\JSON这样的数据表示语言,但比它们占用空间都要小,没有冗余字段。优点在于灵活、解析速度快且易于开发(基于配置自动生成代码),也支持多种语言。一条消息数据使用ProtoBuf序列化后的大小是是二进制序列化的十分之一、JSON的十分之一、是XML的二十分之一。

XML

XML强烈不建议使用,因为它除了无效字符(标签)过多外,解析效率还很低。

网络模块设计

火影手游的PVP网络模块

相对于服务端,客户端不需要处理大量网络数据,单线程就足以满足性能需求。异步Socket回调函数ReceiveCb把收到的消息按顺序存入消息列表msgList中,Update方法将依次读取和处理,在监听表中插入信息。Update会根据监听表和协议名,调用相应的处理方法。

网络模块设计
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,001评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,210评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,874评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,001评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,022评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,005评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,929评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,742评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,193评论 1 309
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,427评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,583评论 1 346
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,305评论 5 342
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,911评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,564评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,731评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,581评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,478评论 2 352

推荐阅读更多精彩内容

  • 1、TCP为什么需要3次握手,4次断开? “三次握手”的目的是“为了防止已失效的连接请求报文段突然又传送到了服务端...
    杰伦哎呦哎呦阅读 3,476评论 0 6
  • 计算机网络概述 网络编程的实质就是两个(或多个)设备(例如计算机)之间的数据传输。 按照计算机网络的定义,通过一定...
    蛋炒饭_By阅读 1,220评论 0 10
  • 网络编程 一.楔子 你现在已经学会了写python代码,假如你写了两个python文件a.py和b.py,分别去运...
    go以恒阅读 2,007评论 0 6
  • 运输层协议概述 从通信和信息处理的角度看,运输层向它上面的应用层提供通信服务,它属于面向通信部分的最高层,同时也是...
    srtianxia阅读 2,406评论 0 2
  • 网络概念第一天 两台电脑怎么通过网络传输数据?怎样才能知道传输的是数据?谁摸过网线? 看电影,怎么看的?通过电流,...
    小吖朱阅读 1,554评论 0 1