
网络游戏通讯模型初探.doc
10页网络游戏通讯模型初探序言网络游戏,作为游戏与网络有机结合的产物,把玩家带入了新的娱乐领域网络游戏在中国开始发展至今也仅有3,4年的历史,跟已经拥有几十年开发历史的 单机游戏相比,网络游戏还是非常年轻的当然,它的形成也是根据历史变化而产生的可以说没有互联网的兴起,也就没有网络游戏的诞生作为新兴产物,网络游 戏的开发对广大开发者来说更加神秘,对于一个未知领域,开发者可能更需要了解的是网络游戏与普通单机游戏有何区别,网络游戏如何将玩家们连接起来,以及如 何为玩家提供一个互动的娱乐环境本文就将围绕这三个主题来给大家讲述一下网络游戏的网络互连实现方法网络游戏与单机游戏说到网络游戏,不得不让人联想到单机游戏,实际上网络游戏的实质脱离不了单机游戏的制作思想,网络游戏和单机游戏的差别大家可以很直接的想到:不就是 可以多人连线吗?没错,但如何实现这些功能,如何把网络连线合理的融合进单机游戏,就是我们下面要讨论的内容在了解网络互连具体实现之前,我们先来了解 一下单机与网络游戏它们各自的运行流程,只有了解这些,你才能深入网络游戏开发的核心现在先让我们来看一下普通单机游戏的简化执行流程:Initialize() // 初始化模块{初始化游戏数据;}Game() // 游戏循环部分{绘制游戏场景、人物以及其它元素;获取用户操作输入;switch( 用户输入数据){case 移动:{处理人物移动;}break;case 攻击:{处理攻击逻辑:}break; ...其它处理响应;...default:break;}游戏的 NPC 等逻辑 AI 处理;}Exit() // 游戏结束{释放游戏数据;离开游戏;}我们来说明一下上面单机游戏的流程。
首先,不管是游戏软件还是其他应用软件,初始化部分必不可少,这里需要对游戏的数据进行初始化,包括图像、声音以 及一些必备的数据接下来,我们的游戏对场景、人物以及其他元素进行循环绘制,把游戏世界展现给玩家,同时接收玩家的输入操作,并根据操作来做出响应,此 外,游戏还需要对 NPC 以及一些逻辑 AI 进行处理最后,游戏数据被释放,游戏结束网络游戏与单机游戏有一个很显著的差别,就是网络游戏除了一个供操作游戏的用户界面平台(如单机游戏)外,还需要一个用于连接所有用户,并为所有用户 提供数据服务的服务器,从某些角度来看,游戏服务器就像一个大型的数据库,提供数据以及数据逻辑交互的功能让我们来看看一个简单的网络游戏模型执行流 程:客户机:Login()// 登入模块{初始化游戏数据;获取用户输入的用户和密码;与服务器创建网络连接;发送至服务器进行用户验证;...等待服务器确认消息;...获得服务器反馈的登入消息;if( 成立 )进入游戏;else提示用户登入错误并重新接受用户登入;}Game()// 游戏循环部分{绘制游戏场景、人物以及其它元素; 获取用户操作输入;将用户的操作发送至服务器;...等待服务器的消息;...接收服务器的反馈信息;switch( 服务器反馈的消息数据 ){case 本地玩家移动的消息:{if( 允许本地玩家移动 )客户机处理人物移动;else客户机保持原有状态;}break;case 其他玩家/NPC 的移动消息:{根据服务器的反馈信息进行其他玩家或者 NPC 的移动处理;}break;case 新玩家加入游戏:{在客户机中添加显示此玩家;}break;case 玩家离开游戏:{在客户机中销毁此玩家数据;}break;...其它消息类型处理;... default:break;}}Exit()// 游戏结束{发送离开消息给服务器;...等待服务器确认;... 得到服务器确认消息;与服务器断开连接;释放游戏数据;离开游戏;}服务器: Listen() // 游戏服务器等待玩家连接模块{...等待用户的登入信息;...接收到用户登入信息;分析用户名和密码是否符合;if( 符合 ){发送确认允许进入游戏消息给客户机; 把此玩家进入游戏的消息发布给场景中所有玩家;把此玩家添加到服务器场景中;}else{断开与客户机的连接; }}Game() // 游戏服务器循环部分{...等待场景中玩家的操作输入;...接收到某玩家的移动输入或 NPC 的移动逻辑输入; // 此处只以移动为例进行此玩家/NPC 在地图场景是否可移动的逻辑判断;if( 可移动 ){对此玩家/NPC 进行服务器移动处理;发送移动消息给客户机;发送此玩家的移动消息给场景上所有玩家;}else发送不可移动消息给客户机; }Exit() // 游戏服务=器结束{接收到玩家离开消息;将此消息发送给场景中所有玩家;发送允许离开的信息;将玩家数据存入数据库;注销此玩家在服务器内存中的数据;}}让我们来说明一下上面简单网络游戏模型的运行机制。
先来讲讲服务器端,这里服务器端分为三个部分(实际上一个完整的网络游戏远不止这些):登入模块、 游戏模块和登出模块登入模块用于监听网络游戏客户端发送过来的网络连接消息,并且验证其合法性,然后在服务器中创建这个玩家并且把玩家带领到游戏模块【狗刨学习网】中;游戏模块则提供给玩家用户实际的应用服务,我们在后面会详细介绍这个部分;在得到玩家要离开游戏的消息后,登出模块则会把玩家从服务器中删除,并且把 玩家的属性数据保存到服务器数据库中,如:经验值、等级、生命值等接下来让我们看看网络游戏的客户端这时候,客户端不再像单机游戏一样,初始化数据后直接进入游戏,而是在与服务器创建连接,并且获得许可的前提下才 进入游戏除此之外,网络游戏的客户端游戏进程需要不断与服务器进行通讯,通过与服务器交换数据来确定当前游戏的状态,例如其他玩家的位置变化、物品掉落 情况同样,在离开游戏时,客户端会向服务器告知此玩家用户离开,以便于服务器做出相应处理以上用简单的伪代码给大家阐述了单机游戏与网络游戏的执行流程,大家应该可以清楚看出两者的差别,以及两者间相互的关系我们可以换个角度考虑,网络游戏 就是把单机游戏的逻辑运算部分搬移到游戏服务器中进行处理,然后把处理结果(包括其他玩家数据) 通过游戏服务器返回给连接的玩家。
网络互连在了解了网络游戏基本形态之后,让我们进入真正的实际应用部分首先,作为网络游戏,除了常规的单机游戏所必需的东西之外,我们还需要增加一个网络通讯模块,当然,这也是网络游戏较为主要的部分,我们来讨论一下如何实现网络的通讯模块一个完善的网络通讯模块涉及面相当广,本文仅对较为基本的处理方式进行讨论网络游戏是由客户端和服务器组成,相应也需要两种不同的网络通讯处理方 式,不过也有相同之处,我们先就它们的共同点来进行介绍我们这里以 Microsoft Windows 2000 [2000 Server]作为开发平台,并且使用 Winsock 作为网络接口(可能一些朋友会考虑使用DirectPlay 来进行网络通讯,不过对于当前游 戏,DirectPlay 并不适合,具体原因这里就不做讨论了) 确定好平台与接口后,我们开始进行网络连接创建之前的一些必要的初始化工作,这部分无论是客户端或者服务器都需要进行让我们看看下面的代码片段: WORD wVersionRequested; WSADATAwsaData; wVersionRequested MAKEWORD(1, 1); if( WSAStartup( wVersionRequested, &wsaData ) !0 ){Failed( WinSock Version Error!" );}上面通过调用 Windows 的 socket API 函数来初始化网络设备,接下来进行网络 Socket的创建,代码片段如下:SOCKET sSocket socket( AF_INET, m_lProtocol, 0 ); if( sSocket == INVALID_SOCKET ) {Failed( "WinSocket Create Error!" );}这里需要说明,客户端和服务端所需要的 Socket 连接数量是不同的,客户端只需要一个 Socket 连接足以满足游戏的需要,而服务端必须为每个玩家 用户创建一个用于通讯的Socket 连接。
当然,并不是说如果服务器上没有玩家那就不需要创建 Socket 连接,服务器端在启动之时会生成一个特殊的 Socket 用来对玩家创建与服务器连接的请求进行响应,等介绍网络监听部分后会有更详细说明有初始化与创建必然就有释放与删除,让我们看看下面的释放部分:if( sSocket != INVALID_SOCKET ) {closesocket( sSocket );}if( WSACleanup() != 0 ){Warning( "Can't release Winsocket" ); }这里两个步骤分别对前面所作的创建初始化进行了相应释放接下来看看服务器端的一个网络执行处理,这里我们假设服务器端已经创建好一个Socket 供使用,我们要做的就是让这个 Socket 变成监听网络连接请求的专用接口,看看下面代码片段: SOCKADDR_IN addr;memset( addr.sin_family = AF_INET;addr.sin_addr.s_addr = htonl( INADDR_ANY );addr.sin_port = htons( Port ); // Port 为要监听的端口号// 绑定 socketif( bind( sSocket, (SOCKADDR*)&addr, sizeof(addr) ) == SOCKET_ERROR ){Failed( "WinSocket Bind Error!");}// 进行监听if( listen( sSocket, SOMAXCONN ) == SOCKET_ERROR ){Failed( "WinSocket Listen Error!");}这里使用的是阻塞式通讯处理,此时程序将处于等待玩家用户连接的状态,倘若这时候有客户端连接进来,则通过 accept()来创建针对此玩家用户的 Socket 连接,代码片段如下: sockaddraddrServer; int nLen sizeof( addrServer ); SOCKET sPlayerSocket accept( sSocket, if( sPlayerSocket == INVALID_SOCKET ){ Failed( WinSocket Accept Error!"); }这里我们创建了 sPlayerSocket 连接,此后游戏服务器与这个玩家用户的通讯全部通过此 Socket 进行,到这里为止,我们服务器已经有了接受玩家用户连接的功能,现在让我们来看看游戏客户端是如何连接到游戏服务器上,代码片段如下: SOCKADDR_IN addr;memset( addr.sin_family = AF_INET;// 要连接的游戏服务器端口号addr.sin_addr.s_addr = inet_addr( IP );// 要连接的游戏服务器 IP 地址,addr.sin_port = htons( Port );//到此,客户端和服务器已经有了通讯的桥梁,//接下来就是进行数据的发送和接收:connect( sSocket, (SOCKADDR*)if( send( sSocket, pBuffer, lLength, 0 ) == SOCKET_ERROR ){Failed( "WinSocket Send Error!");} 这里的 pBuffer 为要发送的数据缓冲指针, lLength 为需要发送的数据长度,通过这支Socket API 函数,我们无论在客户端或者服务端都可以进行数据的发送工作,同时,我们可以通过 recv()这支 Socket API 函数来进。












