如何制作游戏doulci伪服务器激活 自己制作doulcidoulci伪服务器激活

苹果园为iOS用户提供和下载,最新的、、、等,分享最权威的资讯、、及解决办法,拥有最火爆的,苹果园一家专注解决iOS所求的网站。在服务器系统开发时,为了适应数据大并发的请求,我们往往需要对数据进行异步存储,特别是在做分布式系统时,这个时候就不能等待插入数据库返回了取自动id了,而是需要在插入数据库之前生成一个全局的唯一id,使用全局的唯一id,在游戏服务器中,全局唯一的id可以用于将来合服方便,不会出现键冲突。也可以将来在业务增长的情况下,实现分库分表,比如某一个用户的物品要放在同一个分片内,而这个分片段可能是根据用户id的范围值来确定的,比如用户id大于1000小于100000的用户在一个分片内。目前常用的有以下几种:
1,Java&自带的UUID.
UUID.randomUUID().toString(),可以通过服务程序本地产生,ID的生成不依赖数据库的实现。
& &本地生成ID,不需要进行远程调用。
& &全局唯一不重复。
& &水平扩展能力非常好。
& &ID有128 bits,占用的空间较大,需要存成字符串类型,索引效率极低。
& &生成的ID中没有带Timestamp,无法保证趋势递增,数据库分库分表时不好依赖
2,基于Redis的incr方法
Redis本身是单线程操作的,而incr更保证了一种原子递增的操作。而且支持设置递增步长。
&&部署方便,使用简单,只需要调用一个redis的api即可。
& 可以多个服务器共享一个redis服务,减少共享数据的开发时间。
& Redis可以群集部署,解决单点故障的问题。
&如果系统太庞大的话,n多个服务同时向redis请求,会造成性能瓶颈。
3,来自Flicker的解决方案
这个解决方法是基于数据库自增id的,它使用一个单独的数据库专门用于生成id。详细的大家可以网上找找,个人觉得使用挺麻烦的,不建议使用。
4,Twitter Snowflake
snowflake是twitter开源的分布式ID生成算法,其核心思想是:产生一个long型的ID,使用其中41bit作为毫秒数,10bit作为机器编号,12bit作为毫秒内序列号。这个算法单机每秒内理论上最多可以生成)个,也就是大约400W的ID,完全能满足业务的需求。
根据snowflake算法的思想,我们可以根据自己的业务场景,产生自己的全局唯一ID。因为Java中long类型的长度是64bits,所以我们设计的ID需要控制在64bits。
优点:高性能,低延迟;独立的应用;按时间有序。
缺点:需要独立的开发和部署。
比如我们设计的ID包含以下信息:
| 41 bits: Timestamp | 3 bits:&区域&| 10 bits:&机器编号&| 10 bits:&序列号&|
产生唯一ID的Java代码:
*&自定义&ID&生成器
* ID&生成规则: ID长达&64 bits
* | 41 bits: Timestamp (毫秒) | 3 bits:&区域(机房)&| 10 bits:&机器编号&| 10 bits:&序列号&|
public class GameUUID{
& &//&基准时间
& &private long twepoch = 7L; //Thu, 04 Nov :54 GMT
& &//&区域标志位数
& &private final static long regionIdBits = 3L;
& &//&机器标识位数
& &private final static long workerIdBits = 10L;
& &//&序列号识位数
& &private final static long sequenceBits = 10L;
& &//&区域标志ID最大值
& &private final static long maxRegionId = -1L ^ (-1L && regionIdBits);
& &//&机器ID最大值
& &private final static long maxWorkerId = -1L ^ (-1L && workerIdBits);
& &//&序列号ID最大值
& &private final static long sequenceMask = -1L ^ (-1L && sequenceBits);
& &//&机器ID偏左移10位
& &private final static long workerIdShift = sequenceB
& &//&业务ID偏左移20位
& &private final static long regionIdShift = sequenceBits + workerIdB
& &//&时间毫秒左移23位
& &private final static long timestampLeftShift = sequenceBits + workerIdBits + regionIdB
& &private static long lastTimestamp = -1L;
& &private long sequence = 0L;
& &private final long workerId;
& &private final long regionId;
& &public&GameUUID(long workerId, long regionId) {
& & & &//&如果超出范围就抛出异常
& & & &if (workerId & maxWorkerId || workerId & 0) {
& & & & & &throw new IllegalArgumentException("worker Id can't be greater than %d or less than 0");
& & & &if (regionId & maxRegionId || regionId & 0) {
& & & & & &throw new IllegalArgumentException("datacenter Id can't be greater than %d or less than 0");
& & & &this.workerId = workerId;
& & & &this.regionId = regionId;
& &public&GameUUID(long workerId) {
& & & &//&如果超出范围就抛出异常
& & & &if (workerId & maxWorkerId || workerId & 0) {
& & & & & &throw new IllegalArgumentException("worker Id can't be greater than %d or less than 0");
& & & &this.workerId = workerId;
& & & &this.regionId = 0;
& &public long generate() {
& & & &return this.nextId(false, 0);
& & *&实际产生代码的
& & * @param isPadding
& & * @param busId
& & * @return
& &private synchronized long nextId(boolean isPadding, long busId) {
& & & &long timestamp = timeGen();
& & & &long paddingnum = regionId;
& & & &if (isPadding) {
& & & & & &paddingnum = busId;
& & & &if (timestamp & lastTimestamp) {
& & & & & &try {
& & & & & & & &throw new Exception("Clock moved backwards. &Refusing to generate id for " + (lastTimestamp - timestamp) + " milliseconds");
& & & & & &} catch (Exception e) {
& & & & & & & &e.printStackTrace();
& & & & & &}
& & & &//如果上次生成时间和当前时间相同,在同一毫秒内
& & & &if (lastTimestamp == timestamp) {
& & & & & &//sequence自增,因为sequence只有10bit,所以和sequenceMask相与一下,去掉高位
& & & & & &sequence = (sequence + 1) & sequenceM
& & & & & &//判断是否溢出,也就是每毫秒内超过1024,当为1024时,与sequenceMask相与,sequence就等于0
& & & & & &if (sequence == 0) {
& & & & & & & &//自旋等待到下一毫秒
& & & & & & & &timestamp = tailNextMillis(lastTimestamp);
& & & & & &}
& & & &} else {
& & & & & &//&如果和上次生成时间不同,重置sequence,就是下一毫秒开始,sequence计数重新从0开始累加,
& & & & & &//&为了保证尾数随机性更大一些,最后一位设置一个随机数
& & & & & &sequence = new SecureRandom().nextInt(10);
& & & &lastTimestamp =
& & & &return ((timestamp - twepoch) && timestampLeftShift) | (paddingnum && regionIdShift) | (workerId && workerIdShift) |
& &//&防止产生的时间比之前的时间还要小(由于NTP回拨等问题),保持增量的趋势.
& &private long tailNextMillis(final long lastTimestamp) {
& & & &long timestamp = this.timeGen();
& & & &while (timestamp &= lastTimestamp) {
& & & & & &timestamp = this.timeGen();
& &//&获取当前的时间戳
& &protected long timeGen() {
& & & &return System.currentTimeMillis();
使用自定义的这种方法需要注意的几点:
为了保持增长的趋势,要避免有些服务器的时间早,有些服务器的时间晚,需要控制好所有服务器的时间,而且要避免NTP时间服务器回拨服务器的时间;在跨毫秒时,序列号总是归0,会使得序列号为0的ID比较多,导致生成的ID取模后不均匀,所以序列号不是每次都归0,而是归一个0到9的随机数。(本代码参考:/p/6)
上面说的这几种方式我们可以根据自己的需要去选择。在游戏服务器开发中,根据自己的游戏类型选择,比如手机游戏,可以使用简单的redis方式,简单不容易出错,由于这种游戏单服并发新建id量并不太大,完全可以满足需要。而对于大型的世界游戏服务器,它本身就是以分布式为主的,所以可以使用snowflake的方式,上面的snowflake代码只是一个例子,需要自己根据自己的需求去定制,所以有额外的开发量,而且要注意上述所说的注意事项。转载请注明来自,本文地址:
阅读(...) 评论()多游戏服务器架设教程_百度文库
两大类热门资源免费畅读
续费一年阅读会员,立省24元!
多游戏服务器架设教程
&&多游戏服务器架设教程
阅读已结束,下载文档到电脑
想免费下载更多文档?
定制HR最喜欢的简历
下载文档到电脑,方便使用
还剩7页未读,继续阅读
定制HR最喜欢的简历
你可能喜欢简单游戏服务器客服端搭建 - CSDN博客
简单游戏服务器客服端搭建
Login()//&登入模块{ 初始化游戏数据; 获取用户输入的用户和密码; 与服务器创建网络连接; 发送至服务器进行用户验证; ... 等待服务器确认消息; ... 获得服务器反馈的登入消息; if(&成立&)  进入游戏; else  提示用户登入错误并重新接受用户登入;}Game()//&游戏循环部分{ 绘制游戏场景、人物以及其它元素; 获取用户操作输入; 将用户的操作发送至服务器; ... 等待服务器的消息; ... 接收服务器的反馈信息; switch(&服务器反馈的消息数据&) {  case&本地玩家移动的消息:  {   if(&允许本地玩家移动&)    客户机处理人物移动;   else    客户机保持原有状态;  }     case&其他玩家/NPC的移动消息:  {   根据服务器的反馈信息进行其他玩家或者NPC的移动处理;  }    case&新玩家加入游戏:  {   在客户机中添加显示此玩家;  }     case&玩家离开游戏:  {   在客户机中销毁此玩家数据;  }     ...  其它消息类型处理;  ...   default:    }}Exit()//&游戏结束{ 发送离开消息给服务器; ... 等待服务器确认; ... 得到服务器确认消息; 与服务器断开连接; 释放游戏数据; 离开游戏;}&
  服务器:&
Listen()  //&游戏服务器等待玩家连接模块{ ... 等待用户的登入信息; ... 接收到用户登入信息; 分析用户名和密码是否符合; if(&符合&) {  发送确认允许进入游戏消息给客户机;   把此玩家进入游戏的消息发布给场景中所有玩家;  把此玩家添加到服务器场景中; } else {  断开与客户机的连接;& }}Game() //&游戏服务器循环部分{ ... 等待场景中玩家的操作输入; ... 接收到某玩家的移动输入或NPC的移动逻辑输入;& //&此处只以移动为例 进行此玩家/NPC在地图场景是否可移动的逻辑判断;
 if(&可移动&) {  对此玩家/NPC进行服务器移动处理;  发送移动消息给客户机;  发送此玩家的移动消息给场景上所有玩家; } else  发送不可移动消息给客户机;&}Exit()  //&游戏服务=器结束{ 接收到玩家离开消息; 将此消息发送给场景中所有玩家; 发送允许离开的信息; 将玩家数据存入数据库; 注销此玩家在服务器内存中的数据;}}&
  让我们来说明一下上面简单网络游戏模型的运行机制。先来讲讲服务器端,这里服务器端分为三个部分(实际上一个
完整的网络游戏远不止这些):登入模块、游戏模块和登出模块。登入模块用于监听网络游戏客户端发送过来的网络连接
消息,并且验证其合法性,然后在服务器中创建这个玩家并且把玩家带领到游戏模块中;&游戏模块则提供给玩家用户实
际的应用服务,我们在后面会详细介绍这个部分;&在得到玩家要离开游戏的消息后,登出模块则会把玩家从服务器中删
除,并且把玩家的属性数据保存到服务器数据库中,如:&经验值、等级、生命值等。
  接下来让我们看看网络游戏的客户端。这时候,客户端不再像单机游戏一样,初始化数据后直接进入游戏,而是在与
服务器创建连接,并且获得许可的前提下才进入游戏。除此之外,网络游戏的客户端游戏进程需要不断与服务器进行通讯
,通过与服务器交换数据来确定当前游戏的状态,例如其他玩家的位置变化、物品掉落情况。同样,在离开游戏时,客户
端会向服务器告知此玩家用户离开,以便于服务器做出相应处理。
以上用简单的伪代码给大家阐述了单机游戏与网络游戏的执行流程,大家应该可以清楚看出两者的差别,以及两者间相互
的关系。我们可以换个角度考虑,网络游戏就是把单机游戏的逻辑运算部分搬移到游戏服务器中进行处理,然后把处理结
果(包括其他玩家数据)通过游戏服务器返回给连接的玩家。
  在了解了网络游戏基本形态之后,让我们进入真正的实际应用部分。首先,作为网络游戏,除了常规的单机游戏所必
需的东西之外,我们还需要增加一个网络通讯模块,当然,这也是网络游戏较为主要的部分,我们来讨论一下如何实现网
络的通讯模块。
  一个完善的网络通讯模块涉及面相当广,本文仅对较为基本的处理方式进行讨论。网络游戏是由客户端和服务器组成
,相应也需要两种不同的网络通讯处理方式,不过也有相同之处,我们先就它们的共同点来进行介绍。我们这里以
Microsoft&Windows&&Server]作为开发平台,并且使用Winsock作为网络接口(可能一些朋友会考虑使用
DirectPlay来进行网络通讯,不过对于当前在线游戏,DirectPlay并不适合,具体原因这里就不做讨论了)。
  确定好平台与接口后,我们开始进行网络连接创建之前的一些必要的初始化工作,这部分无论是客户端或者服务器都
需要进行。让我们看看下面的代码片段:&
WORD&wVersionR&WSADATAwsaD&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&memset(&&addr,&0,&sizeof(addr)&);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连接,代码片段如下:&
sockaddraddrS&int&nLen&sizeof(&addrServer&);&SOCKET&sPlayerSocket&accept(&sSocket,&&addrServer,&&nLen&);&if(&sPlayerSocket&==&INVALID_SOCKET&){& Failed(&WinSocket&Accept&Error!");&}&
  这里我们创建了sPlayerSocket连接,此后游戏服务器与这个玩家用户的通讯全部通过此Socket进行,到这里为止,
我们服务器已经有了接受玩家用户连接的功能,现在让我们来看看游戏客户端是如何连接到游戏服务器上,代码片段如下
SOCKADDR_IN&memset(&&addr,&0,&sizeof(addr)&);addr.sin_family&=&AF_INET;//&要连接的游戏服务器端口号addr.sin_addr.s_addr&=&inet_addr(&IP&);//&要连接的游戏服务器IP地址,addr.sin_port&=&htons(&Port&);//到此,客户端和服务器已经有了通讯的桥梁,//接下来就是进行数据的发送和接收:connect(&sSocket,&(SOCKADDR*)&addr,&sizeof(addr)&);if(&send(&sSocket,&pBuffer,&lLength,&0&)&==&SOCKET_ERROR&){ Failed(&"WinSocket&Send&Error!");}&
  这里的pBuffer为要发送的数据缓冲指针,lLength为需要发送的数据长度,通过这支Socket&API函数,我们无论在客
户端或者服务端都可以进行数据的发送工作,同时,我们可以通过recv()这支Socket&API函数来进行数据接收:&
if(&recv(&sSocket,&pBuffer,&lLength,&0&)&==&SOCKET_ERROR&){ Failed(&"WinSocket&Recv&Error!");&}&
  其中pBuffer用来存储获取的网络数据缓冲,lLength则为需要获取的数据长度。
  现在,我们已经了解了一些网络互连的基本知识,但作为网络游戏,如此简单的连接方式是无法满足网络游戏中百人
千人同时在线的,我们需要更合理容错性更强的网络通讯处理方式,当然,我们需要先了解一下网络游戏对网络通讯的需
求是怎样的。
  大家知道,游戏需要不断循环处理游戏中的逻辑并进行游戏世界的绘制,上面所介绍的Winsock处理方式均是以阻塞
方式进行,这样就违背了游戏的执行本质,可以想象,在客户端连接到服务器的过程中,你的游戏不能得到控制,这时如
果玩家想取消连接或者做其他处理,甚至显示一个最基本的动态连接提示都不行。
  所以我们需要用其他方式来处理网络通讯,使其不会与游戏主线相冲突,可能大家都会想到:&创建一个网络线程来
处理不就可以了?没错,我们可以创建一个专门用于网络通讯的子线程来解决这个问题。当然,我们游戏中多了一个线程
,我们就需要做更多的考虑,让我们来看看如何创建网络通讯线程。
  在Windows系统中,我们可以通过CreateThread()函数来进行线程的创建,看看下面的代码片段:&
DWORD&dwThreadID;HANDLE&hThread&=&CreateThread(&NULL,&0,&NetThread/*网络线程函式*/,&sSocket,&0,&&dwThreadID&);if(&hThread&==&NULL&){ Failed(&"WinSocket&Thread&Create&Error!");}
  这里我们创建了一个线程,同时将我们的Socket传入线程函数:&
DWORD&WINAPINetThread(LPVOID&lParam)&
{& SOCKET&sSocket&(SOCKET)lP& ...& return&0;&}&
  NetThread就是我们将来用于处理网络通讯的网络线程。那么,我们又如何把Socket的处理引入线程中?
  看看下面的代码片段:&
HANDLE&hEhEvent&=&CreateEvent(NULL,0,0,0);//&设置异步通讯if(&WSAEventSelect(&sSocket,&hEvent,FD_ACCEPT|FD_CONNECT|FD_READ|FD_WRITE|FD_CLOSE&)&==SOCKET_ERROR&){ Failed(&"WinSocket&EventSelect&Error!");}&
  通过上面的设置之后,WinSock&API函数均会以非阻塞方式运行,也就是函数执行后会立即返回,这时网络通讯会以
事件方式存储于hEvent,而不会停顿整支程式。
  完成了上面的步骤之后,我们需要对事件进行响应与处理,让我们看看如何在网络线程中获得网络通讯所产生的事件
WSAEnumNetworkEvents(&sSocket,&hEvent,&&SocketEvents&);if(&SocketEvents.lNetworkEvents&!=&0&){& switch(&SocketEvents.lNetworkEvents&)& {&  case&FD_ACCEPT:   WSANETWORKEVENTS&SocketE   &  case&FD_CONNECT:&  {&   if(&SocketEvents.iErrorCode[FD_CONNECT_BIT]&==&0)&   //&连接成功&    {   //&连接成功后通知主线程(游戏线程)进行处理&   }&  }&   &  case&FD_READ:&  //&获取网络数据&  {   if(&recv(&sSocket,&pBuffer,&lLength,&0)&==&SOCKET_ERROR&)&   {&    Failed(&"WinSocket&Recv&Error!");&   }&  }&   &  case&FD_WRITE:&   &  case&FD_CLOSE:   //&通知主线程(游戏线程),&网络已经断开&   &  default:&   & }}&
  这里仅对网络连接(FD_CONNECT)&和读取数据(FD_READ)&进行了简单模拟操作,但实际中网络线程接收到事件消息后
,会对数据进行组织整理,然后再将数据回传给我们的游戏主线程使用,游戏主线程再将处理过的数据发送出去,这样一
个往返就构成了我们网络游戏中的数据通讯,是让网络游戏动起来的最基本要素。
  最后,我们来谈谈关于网络数据包(数据封包)的组织,网络游戏的数据包是游戏数据通讯的最基本单位,网络游戏
一般不会用字节流的方式来进行数据传输,一个数据封包也可以看作是一条消息指令,在游戏进行中,服务器和客户端会
不停的发送和接收这些消息包,然后将消息包解析转换为真正所要表达的指令意义并执行。
互动与管理
  说到互动,对于玩家来说是与其他玩家的交流,但对于计算机而言,实现互动也就是实现数据消息的相互传递。前面
我们已经了解过网络通讯的基本概念,它构成了互动的最基本条件,接下来我们需要在这个网络层面上进行数据的通讯。
遗憾的是,计算机并不懂得如何表达玩家之间的交流,因此我们需要提供一套可让计算机了解的指令组织和解析机制,也
就是对我们上面简单提到的网络数据包(数据封包)的处理机制。
为了能够更简单的给大家阐述网络数据包的组织形式,我们以一个聊天处理模块来进行讨论,看看下面的代码结构:&
struct&tagMessage{& long&lT& long&lPlayerID;&};&//&消息指令//&指令相关的玩家标识&char&strTalk[256];&//&消息内容&
  上面是抽象出来的一个极为简单的消息包结构,我们先来谈谈其各个数据域的用途:
  首先,lType&是消息指令的类型,这是最为基本的消息标识,这个标识用来告诉服务器或客户端这条指令的具体用途
,以便于服务器或客户端做出相应处理。lPlayerID&被作为玩家的标识。大家知道,一个玩家在机器内部实际上也就是一
堆数据,特别是在游戏服务器中,可能有成千上万个玩家,这时候我们需要一个标记来区分玩家,这样就可以迅速找到特
定玩家,并将通讯数据应用于其上。
  strTalk&是我们要传递的聊天数据,这部分才是真正的数据实体,前面的参数只是数据实体应用范围的限定。
  在组织完数据之后,紧接着就是把这个结构体数据通过Socket&连接发送出去和接收进来。这里我们要了解,网络在
进行数据传输过程中,它并不关心数据采用的数据结构,这就需要我们把数据结构转换为二进制数据码进行发送,在接收
方,我们再将这些二进制数据码转换回程序使用的相应数据结构。让我们来看看如何实现:&
tagMessageM&Msg.lTypeMSG_CHAT;&Msg.lPlayerID&1000;&strcpy(&&Msg.strTalk,&"聊天信息"&);&
  首先,我们假设已经组织好一个数据包,这里MSG_CHAT&是我们自行定义的标识符,当然,这个标识符在服务器和客
户端要统一。玩家的ID&则根据游戏需要来进行设置,这里1000&只作为假设,现在继续:&
char*&p&=&(char*)&Mlong&lLength&=&sizeof(&tagMessage&);&send(&sSocket,&p,&lLength&);//&获取数据结构的长度&
  我们通过强行转换把结构体转变为char&类型的数据指针,这样就可以通过这个指针来进行流式数据处理,这里通过
sizeof()&获得结构体长度,然后用WinSock&的Send()&函数将数据发送出去。
  接下来看看如何接收数据:&
long&lLength&=&sizeof(&tagMessage&);char*&Buffer&=&new&char[lLength];recv(&sSocket,&Buffer,&lLength&);tagMessage*&p&=&(tagMessage*)B//&获取数据&
  在通过WinSock&的recv()&函数获取网络数据之后,我们同样通过强行转换把获取出来的缓冲数据转换为相应结构体
,这样就可以方便地对数据进行访问。(注:强行转换仅仅作为数据转换的一种手段,实际应用中有更多可选方式,这里
只为简洁地说明逻辑)谈到此处,不得不提到服务器/&客户端如何去筛选处理各种消息以及如何对通讯数据包进行管理。
无论是服务器还是客户端,在收到网络消息的时候,通过上面的数据解析之后,还必须对消息类型进行一次筛选和派分,
简单来说就是类似Windows&的消息循环,不同消息进行不同处理。这可以通过一个switch&语句(熟悉Windows&消息循环
的朋友相信已经明白此意),基于消息封包里的lType&信息,对消息进行区分处理,考虑如下代码片段:&
switch(&p-&lType&)&//&这里的p-&lType为我们解析出来的消息类型标识{ case&MSG_CHAT:&//&聊天消息   case&MSG_MOVE:&//&玩家移动消息   case&MSG_EXIT:&//&玩家离开消息   default:  }&
  上面片段中的MSG_MOVE&和MSG_EXIT&都是我们虚拟的消息标识(一个真实游戏中的标识可能会有上百个,这就需要考
虑优化和优先消息处理问题)。此外,一个网络游戏服务器面对的是成百上千的连接用户,我们还需要一些合理的数据组
织管理方式来进行相关处理。普通的单体游戏服务器,可能会因为当机或者用户过多而导致整个游戏网络瘫痪,而这也就
引入分组服务器机制,我们把服务器分开进行数据的分布式处理。
  我们把每个模块提取出来,做成专用的服务器系统,然后建立一个连接所有服务器的数据中心来进行数据交互,这里
每个模块均与数据中心创建了连接,保证了每个模块的相关性,同时玩家转变为与当前提供服务的服务器进行连接通讯,
这样就可以缓解单独一台服务器所承受的负担,把压力分散到多台服务器上,同时保证了数据的统一,而且就算某台服务
器因为异常而当机也不会影响其他模块的游戏玩家,从而提高了整体稳定性。
  分组式服务器缓解了服务器的压力,但也带来了服务器调度问题,分组式服务器需要对服务器跳转进行处理,就以一
个玩家进行游戏场景跳转作为讨论基础:假设有一玩家处于游戏场景A,他想从场景A&跳转到场景B,在游戏中,我们称之
场景切换,这时玩家就会触发跳转需求,比如走到了场景中的切换点,这样服务器就把玩家数据从"游戏场景A&服务器"删
除,同时在"游戏场景B&服务器"中把玩家建立起来。&
  这里描述了场景切换的简单模型,当中处理还有很多步骤,不过通过这样的思考相信大家可以派生出很多应用技巧。
不过需要注意的是,在场景切换或者说模块间切换的时候,需要切实考虑好数据的传输安全以及逻辑合理性,否则切换很
可能会成为将来玩家复制物品的桥梁。&
  本篇讲述的都是通过一些简单的过程来进行网络游戏通讯,提供了一个制作的思路,虽然具体实现起来还有许多要做
,但只要顺着这个思路去扩展、去完善,相信大家很快就能够编写出自己的网络通讯模块。由于时间仓促,本文在很多细
节方面都有省略,文中若有错误之处也望大家见谅。
本文已收录于以下专栏:
相关文章推荐
网络游戏服务器构架设计(一):前言
这篇blog题目涉及的范围真大!以至于在这里需要先写一篇前言把范围缩小。选择写这样一个系列的文章,主要是想给工作了两年的自己一个交代,或者说是一个阶段...
在之前的博客中,我们已经尝试过了在C#环境下,使用Protobuf来序列化Excel表格,然后再代码中反序列化出表格数据【Unity3D —— protobuf 导excel表格数据】,如...
之前我们说过使用Protobuff编解码方式来对网络通信数据进行编码,也就是可以用这种方式来定义通信协议,那么具体要怎么做呢?关于此工具的详细资料和使用操作可以参考我之前写过的博客:Un...
1. test.php 代码如下:1. test.php
require_once './includes/reader.php';
// ExcelFile($filename, $enco...
使用 tomcat 时,相信大家都回遇到中文乱码的问题,具体表现为通过表单取得的中文数据为乱码。
一、初级解决方法
通过一番检索后,许多人采用了如下办法,首先对取得字符串按照 iso8859-1...
一、下载资源:        到MySQL官网下载免安装版的MySQL包,或者直接点击此链接下载:mysql-5.7.17-winx64.zip二、解压配置:        将上面下载的安装包解压到一...
本文小弟浅谈,新手看,老手拍砖,转载请注明出处/ziyebuboka/
    游戏服务器在网游上的作用不容考虑,游戏能做大到什么程度,还是有很大的依靠的...
上篇已经大致完成了redis的下载安装和简单的使用,接下来我们要真正地操作redis进行一些数据的增删改查操作,以及简便好用的Jedis工具。
常用指令:1.增加或者修改已有数据的值:...
他的最新文章
讲师:吴岸城
您举报文章:
举报原因:
原文地址:
原因补充:
(最多只允许输入30个字)}

我要回帖

更多关于 doulci服务器 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信