币乎提示密匙产品密钥过期了之后会怎么办是什么意思


  


    


谈这个话题之前首先要让大家知道,什么是服务器在游戏中,服务器所扮演的角色是同步广播和服务器主动的一些行为,比如说天气NPC AI之类的,之所以现在的很多網络游戏服务器都需要负担一些游戏逻辑上的运算是因为为了防止客户端的作弊行为了解到这一点,那么本系列的文章将分为两部分来談谈网络游戏服务器的设计一部分是讲如何做好服务器的网络连接,同步广播以及NPC的设置,另一部分则将着重谈谈哪些逻辑放在服务器比较合适并且用什么样的结构来安排这些逻辑。

  大多数的网络游戏的服务器都会选择非阻塞select这种结构为什么呢?因为网络游戏嘚服务器需要处理的连接非常之多并且大部分会选择在Linux/Unix下运行,那么为每个用户开一个线程实际上是很不划算的一方面因为在Linux/Unix下的线程是用进程这么一个概念模拟出来的,比较消耗系统资源另外除了I/O之外,每个线程基本上没有什么多余的需要并行的任务而且网络游戲是互交性非常强的,所以线程间的同步会成为很麻烦的问题由此一来,对于这种含有大量网络连接的单线程服务器用阻塞显然是不現实的。对于网络连接需要用一个结构来储存,其中需要包含一个向客户端写消息的缓冲还需要一个从客户端读消息的缓冲,具体的夶小根据具体的消息结构来定了另外对于同步,需要一些时间校对的值还需要一些各种不同的值来记录当前状态,下面给出一个初步嘚连接的结构:

  服务器循环的处理所有连接是一个死循环过程,每次循环都用select检查是否有新连接到达然后循环所有连接,看哪个連接可以写或者可以读就处理该连接的读写。由于所有的处理都是非阻塞的所以所有的Socket IO都可以用一个线程来完成。

  由于网络传输嘚关系每次recv()到的数据可能不止包含一条消息,或者不到一条消息那么怎么处理呢?所以对于接收消息缓冲用了两个指针每次接收都從text_start开始读起,因为里面残留的可能是上次接收到的多余的半条消息然后text_end指向消息缓冲的结尾。这样用两个指针就可以很方便的处理这种凊况另外有一点值得注意的是:解析消息的过程是一个循环的过程,可能一次接收到两条以上的消息在消息缓冲里面这个时候就应该執行到消息缓冲里面只有一条都不到的消息为止,大体流程如下:

  对于消息的处理这里首先就需要知道你的游戏总共有哪些消息,所有的消息都有哪些才能设计出比较合理的消息头。一般来说消息大概可分为主角消息,场景消息同步消息和界面消息四个部分。其中主角消息包括客户端所控制的角色的所有动作包括走路,跑步战斗之类的。场景消息包括天气变化一定的时间在场景里出现一些东西等等之类的,这类消息的特点是所有消息的发起者都是服务器广播对象则是场景里的所有玩家。而同步消息则是针对发起对象是某个玩家经过服务器广播给所有看得见他的玩家,该消息也是包括所有的动作和主角消息不同的是该种消息是服务器广播给客户端的,而主角消息一般是客户端主动发给服务器的最后是界面消息,界面消息包括是服务器发给客户端的聊天消息和各种属性及状态信息

  下面来谈谈消息的组成。一般来说一个消息由消息头和消息体两部分组成,其中消息头的长度是不变的而消息体的长度是可变的,在消息体中需要保存消息体的长度由于要给每条消息一个很明显的区分,所以需要定义一个消息头特有的标志然后需要消息的类型鉯及消息ID。消息头大体结构如下:

  服务器的广播的重点就在于如何计算出广播的对象很显然,在一张很大的地图里面某个玩家在朂东边的一个动作,一个在最西边的玩家是应该看不到的那么怎么来计算广播的对象呢?最简单的办法就是把地图分块,分成大小合適的小块然后每次只象周围几个小块的玩家进行广播。那么究竟切到多大比较合适呢一般来说,切得块大了内存的消耗会增大,切嘚块小了CPU的消耗会增大(原因会在后面提到)。个人觉得切成一屏左右的小块比较合适每次广播广播周围九个小块的玩家,由于广播嘚操作非常频繁那么遍利周围九块的操作就会变得相当的频繁,所以如果块分得小了那么遍利的范围就会扩大,CPU的资源会很快的被吃唍

  切好块以后,怎么让玩家在各个块之间走来走去呢让我们来想想在切换一次块的时候要做哪些工作。首先要算出下个块的周圍九块的玩家有哪些是现在当前块没有的,把自己的信息广播给那些玩家同时也要算出下个块周围九块里面有哪些物件是现在没有的,紦那些物件的信息广播给自己然后把下个块的周围九快里没有的,而现在的块周围九块里面有的物件的消失信息广播给自己同时也把洎己消失的消息广播给那些物件。这个操作不仅烦琐而且会吃掉不少CPU资源那么有什么办法可以很快的算出这些物件呢?一个个做比较顯然看起来就不是个好办法,这里可以参照二维矩阵碰撞检测的一些思路以自己周围九块为一个矩阵,目标块周围九块为另一个矩阵檢测这两个矩阵是否碰撞,如果两个矩阵相交那么没相交的那些块怎么算。这里可以把相交的块的坐标转换成内部坐标然后再进行运算。

  对于广播还有另外一种解决方法实施起来不如切块来的简单,这种方法需要客户端来协助进行运算首先在服务器端的连接结構里面需要增加一个广播对象的队列,该队列在客户端登陆服务器的时候由服务器传给客户端然后客户端自己来维护这个队列,当有人赱出客户端视野的时候由客户端主动要求服务器给那个物件发送消失的消息。而对于有人总进视野的情况则比较麻烦了。

  首先需偠客户端在每次给服务器发送update position的消息的时候服务器都给该连接算出一个视野范围,然后在需要广播的时候循环整张地图上的玩家,找箌坐标在其视野范围内的玩家使用这种方法的好处在于不存在转换块的时候需要一次性广播大量的消息,缺点就是在计算广播对象的时候需要遍历整个地图上的玩家如果当一个地图上的玩家多得比较离谱的时候,该操作就会比较的慢

  同步在网络游戏中是非常重要嘚,它保证了每个玩家在屏幕上看到的东西大体是一样的其实呢,解决同步问题的最简单的方法就是把每个玩家的动作都向其他玩家广播一遍这里其实就存在两个问题:1,向哪些玩家广播广播哪些消息。2如果网络延迟怎么办。事实上呢第一个问题是个非常简单的問题,不过之所以我提出这个问题来是提醒大家在设计自己的消息结构的时候,需要把这个因素考虑进去而对于第二个问题,则是一個挺麻烦的问题大家可以来看这么个例子:

  比如有一个玩家A向服务器发了条指令,说我现在在P1点要去P2点。指令发出的时间是T0服務器收到指令的时间是T1,然后向周围的玩家广播这条消息消息的内容是“玩家A从P1到P2”有一个在A附近的玩家B,收到服务器的这则广播的消息的时间是T2然后开始在客户端上画图,A从P1到P2点这个时候就存在一个不同步的问题,玩家A和玩家B的屏幕上显示的画面相差了T2-T1的时间这個时候怎么办呢?

  有个解决方案我给它取名叫 预测拉扯,虽然有些怪异了点不过基本上大家也能从字面上来理解它的意思。要解決这个问题首先要定义一个值叫:预测误差。然后需要在服务器端每个玩家连接的类里面加一项属性叫latency,然后在玩家登陆的时候对愙户端的时间和服务器的时间进行比较,得出来的差值保存在latency里面还是上面的那个例子,服务器广播消息的时候就根据要广播对象的latency,计算出一个客户端的CurrentTime然后在消息头里面包含这个CurrentTime,然后再进行广播并且同时在玩家A的客户端本地建立一个队列,保存该条消息只箌获得服务器验证就从未被验证的消息队列里面将该消息删除,如果验证失败则会被拉扯回P1点。然后当玩家B收到了服务器发过来的消息“玩家A从P1到P2”这个时候就检查消息里面服务器发出的时间和本地时间做比较如果大于定义的预测误差,就算出在T2这个时间玩家A的屏幕仩走到的地点P3,然后把玩家B屏幕上的玩家A直接拉扯到P3再继续走下去,这样就能保证同步更进一步,为了保证客户端运行起来更加smooth我並不推荐直接把玩家拉扯过去,而是算出P3偏后的一点P4然后用(P4-P1)/T(P4-P3)来算出一个很快的速度S,然后让玩家A用速度S快速移动到P4这样的处理方法是仳较合理的,这种解决方案的原形在国际上被称为(Full plesiochronous)当然,该原形被我篡改了很多来适应网络游戏的同步所以而变成所谓的:预测拉扯。

  另外一个解决方案我给它取名叫 验证同步,听名字也知道大体的意思就是每条指令在经过服务器验证通过了以后再执行动莋。具体的思路如下:首先也需要在每个玩家连接类型里面定义一个latency然后在客户端响应玩家鼠标行走的同时,客户端并不会先行走动洏是发一条走路的指令给服务器,然后等待服务器的验证服务器接受到这条消息以后,进行逻辑层的验证然后计算出需要广播的范围,包括玩家A在内根据各个客户端不同的latency生成不同的消息头,开始广播这个时候这个玩家的走路信息就是完全同步的了。这个方法的优點是能保证各个客户端之间绝对的同步缺点是当网络延迟比较大的时候,玩家的客户端的行为会变得比较不流畅给玩家带来很不爽的感觉。该种解决方案的原形在国际上被称为(Hierarchical

  最后一种解决方案是一种理想化的解决方案在国际上被称为Mutual synchronization,是一种对未来网络的前景的良好预测出来的解决方案这里之所以要提这个方案,并不是说我们已经完全的实现了这种方案而只是在网络游戏领域的某些方面應用到这种方案的某些思想。我对该种方案取名为:半服务器同步大体的设计思路如下:

  首先客户端需要在登陆世界的时候建立很哆张广播列表,这些列表在客户端后台和服务器要进行不及时同步之所以要建立多张列表,是因为要广播的类型是不止一种的比如说囿local message,有remote message,还有global message 等等,这些列表都需要在客户端登陆的时候根据服务器发过来的消息建立好在建立列表的同时,还需要获得每个列表中广播对潒的latency并且要维护一张完整的用户状态列表在后台,也是不及时的和服务器进行同步根据本地的用户状态表,可以做到一部分决策由客戶端自己来决定当客户端发送这部分决策的时候,则直接将最终决策发送到各个广播列表里面的客户端并对其时间进行校对,保证每個客户端在收到的消息的时间是和根据本地时间进行校对过的那么再采用预测拉扯中提到过的计算提前量,提高速度行走过去的方法將会使同步变得非常的smooth。该方案的优点是不通过服务器客户端自己之间进行同步,大大的降低了由于网络延迟而带来的误差并且由于夶部分决策都可以由客户端来做,也大大的降低了服务器的资源由此带来的弊端就是由于消息和决策权都放在客户端本地,所以给外挂提供了很大的可乘之机

  下面我想来谈谈关于服务器上NPC的设计以及NPC智能等一些方面涉及到的问题。首先我们需要知道什么是NPC,NPC需要莋什么NPC的全称是(Non-Player Character),很显然他是一个character,但不是玩家那么从这点上可以知道,NPC的某些行为是和玩家类似的他可以行走,可以战斗可以呼吸(这点将在后面的NPC智能里面提到),另外一点和玩家物件不同的是NPC可以复生(即NPC被打死以后在一定时间内可以重新出来)。其实还有最重要的一点就是玩家物件的所有决策都是玩家做出来的,而NPC的决策则是由计算机做出来的所以在对NPC做何种决策的时候,需偠所谓的NPC智能来进行决策

  下面我将分两个部分来谈谈NPC,首先是NPC智能其次是服务器如何对NPC进行组织。之所以要先谈NPC智能是因为只有當我们了解清楚我们需要NPC做什么之后才好开始设计服务器来对NPC进行组织。

  NPC智能分为两种一种是被动触发的事件,一种是主动触发嘚事件对于被动触发的事件,处理起来相对来说简单一些可以由事件本身来呼叫NPC身上的函数,比如说NPC的死亡实际上是在NPC的HP小于一定徝的时候,来主动呼叫NPC身上的OnDie() 函数这种由事件来触发NPC行为的NPC智能,我称为被动触发这种类型的触发往往分为两种:

一种是由别的物件導致的NPC的属性变化,然后属性变化的同时会导致NPC产生一些行为由此一来,NPC物件里面至少包含以下几种函数:

  这是一个基本的NPC的结构这种被动的触发NPC的事件,我称它为NPC的反射但是,这样的结构只能让NPC被动的接收一些信息来做出决策这样的NPC是愚蠢的。那么怎么样讓一个NPC能够主动的做出一些决策呢?这里有一种方法:呼吸那么怎么样让NPC有呼吸呢?

  一种很简单的方法用一个计时器,定时的触發所有NPC的呼吸这样就可以让一个NPC有呼吸起来。这样的话会有一个问题当NPC太多的时候,上一次NPC的呼吸还没有呼吸完下一次呼吸又来了,那么怎么解决这个问题呢这里有一种方法,让NPC异步的进行呼吸即每个NPC的呼吸周期是根据NPC出生的时间来定的,这个时候计时器需要做嘚就是隔一段时间检查一下哪些NPC到时间该呼吸了,就来触发这些NPC的呼吸

  上面提到的是系统如何来触发NPC的呼吸,那么NPC本身的呼吸频率该如何设定呢这个就好象现实中的人一样,睡觉的时候和进行激烈运动的时候呼吸频率是不一样的。同样NPC在战斗的时候,和平常嘚时候呼吸频率也不一样。那么就需要一个Breath_Ticker来设置NPC当前的呼吸频率

  那么在NPC的呼吸事件里面,我们怎么样来设置NPC的智能呢大体可鉯概括为检查环境和做出决策两个部分。首先需要对当前环境进行数字上的统计,比如说是否在战斗中战斗有几个敌人,自己的HP还剩哆少以及附近有没有敌人等等之类的统计。统计出来的数据传入本身的决策模块决策模块则根据NPC自身的性格取向来做出一些决策,比洳说野蛮型的NPC会在HP比较少的时候仍然猛扑猛打又比如说智慧型的NPC则会在HP比较少的时候选择逃跑。等等之类的

  至此,一个可以呼吸反射的NPC的结构已经基本构成了,那么接下来我们就来谈谈系统如何组织让一个NPC出现在世界里面

  这里有两种方案可供选择,其一:NPC嘚位置信息保存在场景里面载入场景的时候载入NPC。其二NPC的位置信息保存在NPC身上,有专门的事件让所有的NPC登陆场景这两种方法有什么區别呢?又各有什么好坏呢

  前一种方法好处在于场景载入的时候同时载入了NPC,场景就可以对NPC进行管理不需要多余的处理,而弊端則在于在刷新的时候是同步刷新的也就是说一个场景里面的NPC可能会在同一时间内长出来。而对于第二种方法呢设计起来会稍微麻烦一些,需要一个统一的机制让NPC登陆到场景还需要一些比较麻烦的设计,但是这种方案可以实现NPC异步的刷新是目前网络游戏普遍采用的方法,下面我们就来着重谈谈这种方法的实现:

  首先我们要引入一个“灵魂”的概念即一个NPC在死后,消失的只是他的肉体他的灵魂仍然在世界中存在着,没有呼吸在死亡的附近漂浮,等着到时间投胎投胎的时候把之前的所有属性清零,重新在场景上构建其肉体那么,我们怎么来设计这样一个结构呢首先把一个场景里面要出现的NPC制作成图量表,给每个NPC一个独一无二的标识符在载入场景之后,根据图量表来载入属于该场景的NPC在NPC的OnDie() 事件里面不直接把该物件destroy 掉,而是关闭NPC的呼吸然后打开一个重生的计时器,最后把该物件设置为invisable这样的设计,可以实现NPC的异步刷新在节省服务器资源的同时也让玩家觉得更加的真实。

(这一章节已经牵扯到一些服务器脚本相关的東西所以下一章节将谈谈服务器脚本相关的一些设计)

  其主要思路是在广度优先搜索的同时,将下一层的所有节点经过一个启发函數进行过滤一定范围内缩小搜索范围。众所周知的寻路A*算法就是典型的启发式搜索的应用其原理是一开始设计一个Judge(point_t* point)函数,来获得point这个┅点的代价然后每次搜索的时候把下一步可能到达的所有点都经过Judge()函数评价一下,获取两到三个代价比较小的点继续搜索,那些没被選上的点就不会在继续搜索下去了这样带来的后果的是可能求出来的不是最优路径,这也是为什么A*算法在寻路的时候会走到障碍物前面洅绕过去而不是预先就走斜线来绕过该障碍物。如果要寻出最优化的路径的话是不能用A*算法的,而是要用动态规划的方法其消耗是遠大于A*的。

  那么除了在寻路之外,还有哪些地方可以应用到启发式搜索呢其实说得大一点,NPC的任何决策都可以用启发式搜索来做比如说逃跑吧,如果是一个2D的网络游戏有八个方向,NPC选择哪个方向逃跑呢就可以设置一个Judge(int direction)来给定每个点的代价,在Judge里面算上该点的敵人的强弱或者该敌人的敏捷如何等等,最后选择代价最小的地方逃跑下面,我们就来谈谈对于几种NPC常见的智能的启发式搜索法的设計:

  首先获得地图上离该NPC附近的敌人列表设计Judge() 函数,根据敌人的强弱敌人的远近,算出代价然后选择代价最小的敌人进行主动攻击。

  在呼吸事件里面检查自己的HP如果HP低于某个值的时候,或者如果你是远程兵种而敌人近身的话,则触发逃跑函数在逃跑函數里面也是对周围的所有的敌人组织成列表,然后设计Judge() 函数先选择出对你构成威胁最大的敌人,该Judge() 函数需要判断敌人的速度战斗力强弱,最后得出一个主要敌人然后针对该主要敌人进行路径的Judge() 的函数的设计,搜索的范围只可能是和主要敌人相反的方向然后再根据该幾个方向的敌人的强弱来计算代价,做出最后的选择

  这个我并不推荐用A*算法,因为NPC一旦多起来那么这个对CPU的消耗是很恐怖的,而苴NPC大多不需要长距离的寻路只需要在附近走走即可,那么就在附近随机的给几个点,然后让NPC走过去如果碰到障碍物就停下来,这样幾乎无任何负担

  这里有两种方法,一种方法NPC看上去比较愚蠢一种方法看上去NPC比较聪明,第一种方法就是让NPC跟着目标的路点走即可几乎没有资源消耗。而后一种则是让NPC在跟随的时候在呼吸事件里面判断对方的当前位置,然后走直线碰上障碍物了用A*绕过去,该种設计会消耗一定量的系统资源所以不推荐NPC大量的追随目标,如果需要大量的NPC追随目标的话还有一个比较简单的方法:让NPC和目标同步移動,即让他们的速度统一移动的时候走同样的路点,当然这种设计只适合NPC所跟随的目标不是追杀的关系,只是跟随着玩家走而已了

 在这一章节,我想谈谈关于服务器端的脚本的相关设计因为在上一章节里面,谈NPC智能相关的时候已经接触到一些脚本相关的东东了還是先来谈谈脚本的作用吧。

  在基于编译的服务器端程序中是无法在程序的运行过程中构建一些东西的,那么这个时候就需要脚本語言的支持了由于脚本语言涉及到逻辑判断,所以光提供一些函数接口是没用的还需要提供一些简单的语法和文法解析的功能。其实說到底任何的事件都可以看成两个部分:第一是对自身,或者别的物件的数值的改变另外一个就是将该事件以文字或者图形的方式广播出去。那么这里牵扯到一个很重要的话题,就是对某一物件进行寻址恩,谈到这我想将本章节分为三个部分来谈,首先是服务器洳何来管理动态创建出来的物件(服务器内存管理)第二是如何对某一物件进行寻址,第三则是脚本语言的组织和解释其实之所以到苐四章再来谈服务器的内存管理是因为在前几章谈这个的话,大家对其没有一个感性的认识可能不知道服务器的内存管理究竟有什么用。

好久没做过C开发了最近重操旧业。    听说另外一个项目组socket开发遇到问题发送端和接受端数据大小不一致。建议他们采用writen的重发机淛以避免信号中断错误。采用后还是有问题PM让我帮忙研究下。   UNP n年以前看过很久没做过底层开发,手边也没有UNP write中对方socket中断,发送端write会先返回已经发送的字节数,再次write时返回-1,errno号为ECONNRESET(104)   为什么以上测试都是对方已经中断socket后,发送端再次write结果会有所不同呢。从后来找箌的UNP5.12,5.13能找到答案 以上解释了测试12的现象,write一个已经接受到RST的socket,系统内核会发送SIGPIPE给发送进程如果进程catch/ignore这个信号,write都返回EPIPE错误.   因此,UNP建议應用根据需要处理SIGPIPE信号至少不要用系统缺省的处理方式处理这个信号,系统缺省的处理方式是退出进程这样你的应用就很难查处处理進程为什么退出。 Unix系统下如果send在等待协议传送数据时网络断开的话,调用send的进程会接收到一个SIGPIPE信号进程对该信号的默认处理是进程终圵。   在Unix系统下如果recv函数在等待协议接收数据时网络断开了,那么调用recv的进程会接收到一个SIGPIPE信号进程对该信号的默认处理是进程终圵。   处理方法:   在初始化时调用signal(SIGPIPE,SIG_IGN)忽略该信号(只需一次)   其时send或recv函数将返回-1errno为EPIPE,可视情况关闭socket或其他处理   gdb:   gdb默认收到sigpipe时中断程序可调用handle print   相关   (1)SIG_DFL信号专用的默认动作:   (a)如果默认动作是暂停线程,则该线程的执行被暂时挂起当线程暂停期间,发送给线程的任何附加信号都不交付直到该线程开始执行,但是SIGKILL除外   (b)把挂起信号的信号动作设置成SIG_DFL,且其默认动作是忽略信号 (SIGCHLD)   (2)SIG_IGN忽略信号   (a)该信号的交付对线程没有影响   (b)系统不允许把SIGKILL或SIGTOP信号的动作设置为SIG_DFL   (3)指向函数的指针--捕获信号   (a)信号一经交付,接收线程就在指定地址上执行信号捕获程序在信号捕 )函数所生成,则从信号SIGFPE,SIGILL,SIGSEGV的信号捕获函数正常返回后线程的行为是未定义的   (d)系统不允许线程捕获SIGKILL和SIGSTOP信号。   (e)如果线程为SIGCHLD信号建立信号捕获函数而该线程有未被等待的以终止的子线程时,没有规定是否要生成SIGCHLD信號来指明那个子线程   每一种信号都被OSKit给予了一个符号名,对于32位的i386平台而言一个字32位,因而信号有32种下面的表给出了常用的符號名、描述和它们的信号值。   符号名 保留作为用户自定义的信号是    注意:Linux信号机制基本上是从Unix系统中继承过来的早期Unix系统中的信号机制比较简单和原始,后来在实践中暴露出一些问题因此,把那些建立在早期机制上的信号叫做"不可靠信号"信号值小于SIGRTMIN(Red hat 7.2中,SIGRTMIN=32SIGRTMAX=63)的信号都是不可靠信号。这就是"不可靠信号"的来源它的主要问题是:进程每次处理信号后,就将对信号的响应设置为默认动作在某些情況下,将导致对信号的错误处理;因此用户如果不希望这样的操作,那么就要在信号处理函数结尾再一次调用signal()重新安装该信号。 另外我再做一些补充,产生RST响应以至于系统发出SIGPIPE信号应该分为两种情况:   1. 客户端到服务端之间网络断掉,或者服务端断电等物理连接斷掉了,这种情况下客户端不会退出send函数正常执行,不会感觉到自己出错因为由于物理网络断开,服务端不会给客户端回应错误消息没有RST响应,自然也不会产生SIGPIPE信号但是当服务端再恢复正常的时候,对客户端send来的消息会产生RST响应客户端就收到SIGPIPE信号了,程序退出泹是这时send函数是能够返回 -1的。可以进行异常处理   2.客户端到服务端的网络能通,服务程序挂掉客户端程序会马上退出,因为服务端能正常返回错误消息客户端收到,SIGPIPE信号就产生了不过我不确定此时服务端返回是的RST响应,抓包来看没有RST标志水平有限,只写到这了


今天主要讲游戏的登陆流程,
由于我们的后台架构是前端接入层+后端业务进程的架构模式因此,任何网络连接请求的数据都要经过湔端接入。
首先要说明目前大多数游戏都是 账号+角色的模式。ok
登陆两种模式1已在该服创建过账号,创建过角色的玩家
2.在该服没有创建過账号数据库是没有数据

我们首先说一下我们的进程模式

连接层+业务层+认证层


比如,当一个玩家要玩我们的游戏首先,客户端发送上來的协议字段是没有具体的账号信息的
因此我们认为该账号没哟经过认证过的,所以通过业务层的一个特殊方法将协议数据转发给认證层,
那么认证层做什么事情他首先去查账号数据库表,是否该账号在该服建立过
1,新人数据库是不纯在。那么我们将上送来的账號插入到数据库同时会根据数据库生成一个uin,这个uin是所有唯一的长度是8个字节,
使用uin标识改账号的唯一然后通过一些细节处理,比洳加密打乱等简单处理,在讲该数据保存到账号cach中,发给前端接入层
然后在将基本的已认证信息发给玩家,当玩家收到该数据后表示,该玩家已经在该服有账号了然后,通过账号登陆将基本账号数据发送给业务进程,

表示认证通过我们会通过uin,然后在通过請求,从角色数据表中查询是否有已有角色创建下发角色列表。


创建角色业务进程将会从玩家内存池中,分配一段内存给该角色。

2.峩们会首先从账号缓存中取得数据如果存在,就认证成功如果不存在,就去查数据库然后判断是否存在,在走上诉流程

3如果玩家巳在该服玩过,那么我们会直接从数据库把基本数据插入到缓存里

这些基本的一个登陆流程了。

inet_addr返回的整数形式是网络字节序而inet_network返回嘚整数形式是主机字节序。他俩都有一个小缺陷
那就是当IP是255.255.255.255时,这两个函数会认为这是个无效的IP地址这是历史遗留问题,其实在目前夶部
inet_aton函数和上面这俩个函数的区别就是在于他认为255.255.255.255是有效的他不会冤枉这个看似特殊的IP地址。对了inet_aton函数返回的是网络字节序的IP地址。

垺务端程序对于内存的管理上是重中之中如何管理好程序的内存是保证程序稳定的最重要因素。

因此我们是如何做的呢。

1.我们知道當有一个新玩家进入游戏,我们需要分配一段内存给这个玩家当这个玩家下线了,不玩了我们就要对这段内存


进行清理。因此如何囿效的管理这段内存,如何能重新利用这段内存是我们的问题,因此使用内存池的方式,是比较理想的
通过内存池我们可以预分配┅大块数据使用,下线的玩家之后那段内存是可以重新使用的。
目前游戏中使用到内存池有玩家,
2.固定不变的数据游戏里有配置表數据,这些数据是玩家在游戏过程中需要使用的数据,比如任务表装备表的数据等等,这些数据是固定不变的
因此我们就放在一个凅定的数组里,一张二维表数据相对于数组而言就是二维数组,因此定义响应表的二维数组。当系统系统启动的时候加载进去,并排好序
3.为了快速查找的数据,我们使用hash内存查找速度几乎是常数。

为了我们的程序具有coredump的时候玩家的数据不会丢失,因此我们的方案使用了共享内存的方式,即使程序coredump了我们整个游戏的数据和core之前的数据保持一致。

经过以上描述我们的服务端,使用了主要技术為1共享内存,内存池hash内存,二分查找算法等


经过实践,我们的游戏是非常稳定不会丢失数据等状况

最近因为项目的makefile同时使用了静態动态的连接库,所以就要同事的链接进去

我们知道gcc的-static选项可以使链接器执行静态链接。但简单地使用-static显得有些’暴力’因为他会把命令行中-static后面的所有-l指明的库都静态链接,更主要的是有些库可能并没有提供静态库(.a),而只提供了动态库(.so)这样的话,使用-static就會造成链接错误

之前的链接选项大致是这样的,

  其中用到的两个选项:-Wl,-Bstatic和-Wl,-Bdynamic这两个选项是gcc的特殊选项,它会将选项的参数传递给链接器作为链接器的选项。比如-Wl,-Bstatic告诉链接器使用-Bstatic选项该选项是告诉链接器,对接下来的-l选项使用静态链接;-Wl,-Bdynamic就是告诉链接器对接下来的-l選项使用动态链接下面是man

  值得注意的是对-static的描述:-static和-shared可以同时存在,这样会创建共享库但该共享库引用的其他库会静态地链接到該共享库中。


在《UNIX网络编程第1卷》中也有详细的阐述:

SO_KEEPALIVE 保持连接检测对方主机是否崩溃避免(服务器)永远阻塞于TCP连接的输入。设置该選项后如果2小时内在此套接口的任一方向都没有数据交换,TCP就自 动给对方 发一个保持存活探测分节(keepalive probe)这是一个对方必须响应的TCP分节.它会導致以下三种情况:对方接收一切正常:以期望的ACK响应。2小时后TCP将发出另一个探测分 节。对方已崩溃且已重新启动:以RST响应套接口的待处理错误被置为ECONNRESET,套接 口本身则被关闭对方无任何响应:源自berkeley的TCP发送另外8个探测分节,相隔75秒一个试图得到一个响应。在发出第一個探测分节11分钟 15秒后若仍无响应就放弃套接口的待处理错误被置为ETIMEOUT,套接口本身则被关闭如ICMP错误是“host unreachable(主机不可达)”,说明对方主机并沒有崩溃但是不可达,这种情况下待处理错误被置为

根据上面的介绍我们可以知道对端以一种非优雅的方式断开连接的时候我们可以設置SO_KEEPALIVE属性使得我们在2小时以后发现对方的TCP连接是否依然存在。   

如果我们不能接受如此之长的等待时间从TCP-Keepalive-HOWTO上可以知道一共有两种方式可以設置,一种是修改内核关于网络方面的 配置参数另外一种就是SOL_TCP字段的TCP_KEEPIDLE, TCP_KEEPINTVL


我所搭建的总体架构非常简单就是
前端接入进程 +后端业务逻辑處理进程+数据库缓存进程+其他协作进程
1.通信组件 所有进程使用的是单线程,没有使用其他线程进程间通信使用我们的一个自主开发的通信组件。
2.数据协议组件 我们也是自主开发的一个以xml表现形式通过工具生成.h头文件,二进制bin文件数据加载文件等。也可以和数据库进行茭互的数据文件也可以进行网络传输等,功能十分强大

游戏的基本组件详细实现就不介绍了,本文主要介绍如何通过这些很好的组件來搭建可用性稳定性,可扩展性的一个游戏项目


单独进程的游戏内部函数运行状态有多种,比如收到某个信号等下面主要介绍一下幾个回调函数

1.进程初始化函数,也就是该函数在程序启动的时候对配置文件和一些组件等初始化。


2.时间驱动函数该函数会每秒钟运行┅次。
3.消息处理函数实质是一个死循环,不停的的接收其他模块或者网络过来的消息进行处理
4.重载函数。当该进程有一些配置等需偠热更新的东西,可以直接调用该函数进行重载
5.进程退出函数当收到某个信号后,进程需要安全退出的
ok当我们把这几种模块回调函数萣义好,框架基本就可以起来了然后通过初始化一些组件信息,用来进行进程间通信数据描述等进行了启动进程
目前已经有了具体的湔端接入的进程,通过组件的共享内存方式发送到后端业务进程
对于我们的mmorpg游戏来说,地图是最基础的东西了当玩家进入游戏,首先進入的是一个世界的概念就是在一张张地图上来跑动,和其他玩家进行互动
来模拟真实的生活环境。
那么我们的地图是如和实现呢
艏先要知道地图是以像素为单位,在一个二维的平面地图上每个像素点都是有坐标的,但是每张地图非常巨大不可能把每个坐标点记錄下来。因此我们程序就要对一些点的集合进行处理
既要游戏的体验感好,又要程序处理速度快就要选择合理的小格子。就我经历过嘚项目而言有菱形的,有正方形的有矩形的等等,根据需求进行处理我们的是矩形,边长为30*50像素的矩形
而这个矩形就代表了很多信息,我们通过用一个字节来表达这个格子的信息比如第一位是阻挡信息,第二位是技能层等等有八位供选择。前后端要约定好
然后当美术给我们了一张地图,我们会开发一个地图编辑器工具将地图按照规定的小格子,和前后端约定好的数据信息生成一个地图mask文件。提供给后端使用
ok,一些基本的数据信息我们已经得到了我们就需要提供一些具体的函数了,通过给我们的mask文件进行一些必要校驗,比如我们在地图行走,下一点是否是阻碍点
当我们释放某些技能,是否能通过是否到达了地图的边界,扇形攻击的角度范围等等函数,这里是需要一些数学的基础知识等
为了达到我们游戏的一个体验感比较真实的效果和计算机速度等因素,我们对地图有进行叻一个更大区域的划分叫动态区域。每个动态区域都有固定的大小这是根据我们游戏屏幕进行划分的。将屏幕划分为九个区域每个區域主要是包含了一些实体的内存实例id,比如当玩家进入这个区域我们会将该玩家的内存id加入该区域呢离开就删除。这是个动态的过程

每张地图都是有一个tick,也就是上文我们提到的tick

也就是我们保证,当玩家看到你了一定是我也能看到对方。这个时候我们就通过这寫来调用数据包发送协议,将自己的数据发送到对方

因为有个tick,因此在内存中的玩家,每秒钟都会进行tick每秒钟都会进行对玩家的所茬地图,进行动态区域的更新

同样,怪物也是同样的道理我们通过tick,对地图上的怪物进行处理如果怪物死亡,就将其id删除更新视野。然后根据具体业务具体处理了

如何通过计算机然后表现在地图上呢。


首先我们知道我们所有的实体在计算机里,其实都是一段内存表现通过这段内存,记录了我们所有的需要记录的数据比如账号名,在线信息等等
那么,我们首先预分配好一个巨大的内存池當有个新的账号登陆的时候,我们就在内存池里取一段内存此时我们已经记录到了数据库了。
同样我们也分配了怪物的内存,等等
技能系统算是所有系统里最复杂的东西了。设计的东西非常多比如当玩家打出去一个技能,伤害mp,状态等等等等都会对伤害进行修囸,打出去有什么效果
在地图上怎么表现,能否穿过障碍等等
任务系统主要是有接受,完成奖励的等,整体来说不是困难
也是最複杂的一个,主要是背包的一些操作吧整理,堆叠移动等等设计大量的算法等。

还有很多系统目前所有的系统我都设计开发,目前主要是致力于防刷性能提升,bug漏洞修复等等


下篇文章可能主要介绍网络接入这块,还有具体的登陆这块
开发游戏还是蛮有乐趣的以後有心得继续给大家分享。

一、TCP的滑动窗口大小实际上就是socket的接收缓冲区大小的字节数

二、对于server端的socket一定要在listen之间设置缓冲区大小因为,accept时新产生的socket会继承监听socket的缓冲区大小对于client端的socket一定要在connet之前设置缓冲区大小,因为connet时需要进行三次握手过程会通知对方自己的窗口夶小。在connet之后再设置缓冲区已经没有什么意义。

三、由于缓冲区大小在TCP头部只有16位来表示所以它的最大值是65536,但是对于一些情况来说需要使用更大的滑动窗口这时候就要使用扩展的滑动窗口,如光纤高速通信网络或者是卫星长连接网络,需要窗口尽可能的大这时會使用扩展的32位的滑动窗口大小。

四、滑动窗口听移动规则:

1、窗口合拢:在收到对端数据后自己确认了数据的正确性,这些数据会被存储到缓冲区等待应用程序获取。但这时候因为已经确认了数据的正确性需要向对方发送确认响应ACK,又因为这些数据还没有被应用进程取走这时候便需要进行窗口合拢,缓冲区的窗口左边缘向右滑动注意响应的ACK序号是对方发送数据包的序号,一个对方发送的序号鈳能因为窗口张开会被响应(ACK)多次。

2、窗口张开:窗口收缩后应用进程一旦从缓冲区中取出数据,TCP的滑动窗口需要进行扩张这时候窗口的右边缘向右扩张,实际上窗口这是一个环形缓冲区窗口的右边缘扩张会使用原来被应用进程取走内容的缓冲区。在窗口进行扩张後需要使用ACK通知对端,这时候ACK的序号依然是上次确认收到包的序号

3、窗口收缩,窗口的右边缘向左滑动称为窗口收缩,Host Requirement RFC强烈建议不偠这样做但TCP必须能够在某一端产生这种情况时进行处理

      在游戏的世界里,玩家在地图上的某点是否能够走动,是否遇到障碍是否是赱到了阴影处,是否水层等等先关信息都要我们前后端知道

那么服务器是如何进行实现的呢。下面主要给大家讲讲

     首先,我们知道图爿是以像素为主要为单位进行计量但是我们后端又不能使用这个东西,在二维的世界观里我们是以坐标(x,y)具体的表现出其某个东西,所在的位置因此,我们就要通过这个像素来表达出地点

      想象一下啊,当我们确定到一个坐标的时候但我们将其慢慢变大,那个小點就开始显示长宽因此我们也用其原理。因此我们是以将地图划分为很多个小格子,这些小格子其实就代表了所谓的一个点,那么這个小格子是多大呢这里我们一不超过50的为单位,作为长和宽

      那这个小格子怎么样去表达具体的信息呢,处每个因此,我们约定鼡1个字节来表其信息,一个字节八位每个位具体可以表示什么含义,比如第一位如果0表示可行走,1表示障碍第二位0表示无遮掩,1表礻遮掩等,这里我就不一一举出不同游戏有不同的具体表达信息。

       好那么我们划分了很多歌小格子,每一行都有相同的小格子那麼我们就知道了这个地图,长有多少个各自高有多少个格子。


在这里我们用了正方形表达,其实我们可以用长方形我还见过菱形的,各个游戏不一样 然后,头信息主要是这些然后,将通过地图编辑器把每个格子根据地图的基本信息,画图然后通过工具具体生荿。

头+包体就生成了msk文件。

当前端生成了msk后我们后端开始对其进行数据解析了。

根据msk二进制文件后端进行解析,因为我们每个地圖,还有个基本的信息配置表因此,我们就根据其掩码信息将其一些数据附加到地图的结构体里。

比如地图最大坐标,地图大小按照我们的规定,这个地图有哪些动态区域(动态区域,以后回去讲解)等等数据信息,供以后我们在地图上使用

在这里地图掩码主要讲解完毕。

以后会将我们是如何在地图上,看到玩家的

epoll_create创建一个属于该文件系统的文件,然后返回其文件描述符

描述符都分配┅个该结构体。该结构的各个成员的定义如下注释也很详细。

而通过epoll_ctl接口加入该epoll描述符监听的套接字则属于socket filesystem这点一定要注意。每个添加的待监听(这里监听

和listen调用不同)都对应于一个epitem结构体该结构体已红黑树的结构组织,eventpoll结构中保存了树的根节点(rbr成员)

同时有监聽事件到来的套接字的该结构以双向链表组织起来,链表头也保存在eventpoll中(rdllist成员)

epoll_create的调用很简单,就是创建一个epollevent的文件并返回文件描述苻。

epoll_ctl用来添加删除以及修改监听项。

同样代码很清楚。先来看看添加流程

这是epoll机制中唯一对监听套接字调用poll时第2个参数不为NULL的时机ep_ptable_queue_proc函数的作用是注册等待函数

并添加到指定的等待队列,所以在第一次调用后该信息已经存在了,无需在poll函数中再次调用了

那么该poll函数箌底是怎样的呢,这就要看我们在传入到epoll_ctl前创建的套接字的类型(socket调用)对于创建的tcp套接字

来说,可以按照创建流程找到其对应得函数昰tcp_poll

  1. 如果poll table回调函数存在(ep_ptable_queue_proc),则调用它来等待注意这只限第一次调用,在后面的poll中都无需此步
  2. 判断事件的到达(根据tcp的相关成员)

tcp_poll注冊到的等待队列是sock成员的sk_sleep,等待队列在对应的IO事件中被唤醒当等待队列被唤醒时会调用相应的等待回调函数

,前面看到我们注册的是函數ep_poll_callback该函数可能在中断上下文中调用。

注意这里有2中队列一种是在epoll_wait调用中使用的eventpoll的等待队列,用于判断是否有监听套接字可用一种是對应于每个套接字

的等待队列sk_sleep,用于判断每个监听套接字上事件该队列唤醒后调用ep_poll_callback,在该函数中又调用wakeup函数来唤醒前一种

队列来通知epoll_wait調用进程。

eventpoll中巧妙地设置了2种类型的锁一个是mtx,是个mutex类型是对该描述符操作的基本同步锁,可以睡眠;所以又存在了另外一个

由于中斷的到来时异步的为了方便,先看ep_send_events函数

该函数的注释也很清晰,不过我们从总体上分析下

首先函数加mtx锁,这时必须的

然后得工作昰要读取ready queue,但是中断会写这个成员所以要加spinlock;但是接下来的工作会sleep,所以在整个loop都加spinlock显然

会阻塞ep_poll_callback函数从而阻塞中断,这是个很不好的荇为也不可取。于是epoll中在eventpoll中设置了另一个成员ovflist在读取ready

queue前,我们设置该成员为NULL然后就可以释放spinlock了。为什么这样可行呢因为对应的,茬ep_poll_callback中获取spinlock后,对于

这样下次中断中的事件可以放入ready queue了最后判断是否有其他epoll_wait调用被阻塞,则唤醒

从源代码中,可以看出epoll的几大优点:

  1. 鼡户传入的信息保存在内核中了无需每次传入
  2. 事件监听机制不在是 整个监听队列,而是每个监听套接字在有事件到达时通过等待回调函數异步通知epoll然后再返回给用户。

同时epoll中的同步机制也是一个内核编程的设计经典值得深入理解。

摘要: 本文作为游戏服务器端开发的基夲大纲是游戏实践开发中的总结。第一部分专业基础用于指导招聘和实习考核, 第二部分游戏入门讲述游戏服务器端开发的基本要點,第三部分服务端架构介绍架构设计中的一些基本原则。希望能帮到大家

建立连接的三次握手与断开连接的四次握手
连接建立与断开過程中的各种状态
TCP/IP协议的传输效率

1)请解释DOS攻击与DRDOS攻击的基本原理
1.1.2 掌握常用的网络通信模型
Epoll边缘触发与平台出发点区别与应用
计算机文件系统,页表结构
内存池与对象池的实现原理应用场景与区别
关系数据库MySQL的使用
对C/C++语言有较深的理解
深刻理解接口,封装与多态并且囿实践经验
深刻理解常用的数据结构:数组,链表二叉树,哈希表
熟悉常用的算法及相关复杂度:冒泡排序快速排序
不要相信客户端數据,一定要检验作为服务器端你无法确定你的客户端是谁,你也不能假定它是善意的请做好自我保护。(这是判断一个服务器端程序員是否入门的基本标准)
务必对于函数的传人参数和返回值进行合法性判断内部子系统,功能模块之间不要太过信任要求低耦合,高内聚
插件式的模块设计模块功能的健壮性应该是内建的,尽量减少模块间耦合
道法自然不要迷信,迷恋设计模式更不要生搬硬套
简化,简化再简化,用最简单的办法解决问题
借大宝一句话:设计本天成妙手偶得之
自定义文件存储,如《梦幻西游》
选择存储系统要考慮到因素:稳定性性能,可扩展性
使用内存池和对象池禁止运行期间动态分配内存
对于输入输出的指针参数,严格检查宁滥勿缺
防圵读内存溢出,确保字符串以’\0’结束
简单高效大量日志操作不应该影响程序性能
稳定,做到服务器崩溃是日志不丢失
完备玩家关键操作一定要记日志,理想的情况是通过日志能重建任何时刻的玩家数据
开关开发日志的要加级别开关控制
JSON,文本协议简单,自解释無联调成本,扩展性好也很方便进行包过滤以及写日志
自定义二进制协议,精简有高效的传输性能,完全可控几乎无扩展性
方便追蹤道具,装备流向
每个角色装备,道具都应对应有全局唯一Key
消息队列进行同步化处理
合并, 同一帧内的数据包进行合并减少IO操作次数
单副本, 用一个包尽量只保存一份,减少内存复制次数
AOI同步中减少中间过程无用数据包
随时监控服务器内部状态
内存池对象池使用情况
各种業务逻辑的处理次数
基于每个玩家每条协议的包频率控制,瘫痪变速齿轮
每个模块都有开关可以紧急关闭任何出问题的功能模块
2.15 反外挂反作弊
包频率控制可以消灭变速齿轮
包id自增校验,可以消灭WPE
包校验码可以消灭包拦截篡改
图形识别吗可以踢掉99%非人的操作
核心配置逻辑嘚热更新,如防沉迷系统包频率控制,开关控制等
代码基本热更新如Erlang,Lua等
关键系统资源(如元宝精力值,道具装备等)的产出记ㄖ志
资源的产出和消耗尽量依赖两个或以上的独立条件的检测
严格检查各项操作的前置条件
系统底层与具体业务逻辑无关,可以用大量的機器人压力测试暴露各种bug确保稳定
系统性的保证游戏不会崩溃
IO操作合并缓写 (事务性的提交db操作,包合并文件日志缓写)
减少竞态条件 (避免频繁进出切换,尽量减少锁定使用多线程不一定由于单线程) 多线程不一定比单线程快
自己测试,用数据说话别猜
接口支持:实時查询,控制指令数据监控,客服处理等
实现考虑提供Http接口
2.21 容灾与故障预案
3.1 什么是好的架构
能迅速的实现策划需求,响应需求变更
简囮开发将复杂性控制在架构底层,降低对开发人员的技术要求逻辑开发不依赖于开发人员本身强大的技术实力,提高开发效率
3.2 架构实踐的思考
简单满足需求的架构就是好架构
设计性能,抓住重要的20% 没必要从程序代码里面去抠性能
人难免会犯错,尽可能的用一套机制詓保障逻辑的健壮性

游戏的设计是一项颇有挑战性的工作游戏服务器的发展也由以前的单服结构转变为多服机构,甚至出现了bigworld引擎的解決方案最近了解到Unreal的服务器解决方案atlas也是基于集群的方式。

是一个很复杂的课题这里暂不谈bigworld和atlas的这类服务器的设计,更多的是基于功能和场景划分服务器结构

首先说一下思路,服务器划分基于以下原则:

  1. 分离游戏中占用系统资源(cpu内存,IO等)较多的功能独立成服務器。
  2. 在同一服务器架构下的不同游戏应尽可能的复用某些服务器(进程级别的复用)。
  3. 以多线程并发的编程方式适应多核处理器
  4. 宁鈳在服务器之间多复制数据,也要保持清晰的数据流向
  5. 主要按照场景划分进程,若需按功能划分必须保持整个逻辑足够的简单,并满足以上12点。

各个服务器的简要说明:

Gateway 是应用网关主要用于保持和client的连接,该服务器需要2种IO对client采用高并发连接,低吞吐量的网络模型如IOCP等,对服务器采用高吞吐量连接如阻塞或异步IO。

  1. 同时也分担了网络消息包的加解密,压缩解压等cpu密集的操作
  2. 隔离了client和内部服务器组,对client来说它只需要知道网关的相关信息即可(ip和port)。
  3. client由于一直和网关保持常连接所以切换场景服务器等操作对client来说是透明的。

World Server 是┅个控制中心它负责把各种计算资源分布到各个服务器,它具有以下职责:

  1. 管理和维护多个功能服务器主要是同步数据到功能服务器。
  2. 复杂转发其他服务器和Gateway之间的数据
  3. 实现其他需要跨场景的功能,如组队聊天,帮派等

所有玩家的移动类操作都在该服务器上做检查,所以该服务器本身具备所有地图的地形等相关信息具体检查过程是这样的:首先,Worldserver收到一个移动信息WorldServer收到后向Phys Server请求检查,Phys Server检查成功后再返回给world Server然后world server传递给相应的Scene Server。

Scene Server 场景服务器按场景划分,每个服务器负责的场景应该是可以配置的理想情况下是可以动态调节的。

ItemMgr Server 物品管理服务器负责所有物品的生产过程。在该服务器上存储一个物品掉落服务器初始化的时候载入到内存。任何需要产生物品的垺务器均与该服务器直接通信

AIServer 又一个功能服务器,负责管理所有NPC的AIAI服务器通常有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 类似。额外多两条接入与离开遊戏网络即可

 这两天在看项目的数据结构定义及关系,遇到一些关于socket的知识点还有一些C++的知识点,下面总结下:

    结构体epoll_event 被用于注册所感兴趣的事件和回传所发生待处理的事件定义如下:

}
该怎么恢复啊... 该怎么恢复啊

这个昰你的钥匙产品密钥过期了之后会怎么办了需要重新激活

你对这个回答的评价是?


不提示me时产品密钥过期了之后会怎么办是他的日期有鈳能过了

你对这个回答的评价是


主要的意思的话就是要失去提前过节的原因吧。

你对这个回答的评价是

下载百度知道APP,抢鲜体验

使用百度知道APP立即抢鲜体验。你的手机镜头里或许有别人想知道的答案

}
 

    的域名算是一个挺好的品牌化起步,不忘初码聚栈前行。

【】 在2018年初已经在考虑新的创业转型,觉得自己的积累够了想正儿八经的来做产品了,做自主研发的互聯网产品能有持续的用户和收益,自循环发展在这样一个十字路口,机缘巧合遇到了老牌互联网公司XX胡同的老刘总交换了想法,策劃了方案很快一拍即合达成了深度的合作,将公司团队主要人员转入XX胡同我在XX胡同担任副总裁,负责团队管理建设以及所构思的新产品的研发推进

虽说XX胡同属于没落的互联网老兵,但也手握一定流量和品牌资源正当我准备猛施拳脚大干一场的时候,就像电视剧情节┅样XX胡同发生了内斗,用一段话来描述就是:3年前因为吃拿卡要和性骚扰女员工而被开除的原赵X副总得到他的情人相好-XX资本合伙人同時也是XX胡同董事长的周XX女士的支持,回来篡夺了总裁岗位而董事会在毫无征兆的情况下开除了服务XX胡同15年的Hunter老刘总。

且不说开除是否合悝又或是原老总干的好与坏,就这背后的人物故事关系着实狗血满满的恶意,和老刘总好不容易推进的计划和初见雏形的新团队就這样给冲散了。18年是运气倒退的一年又一次的十足路口,我要做出新的选择了

做公司域名到起了初码这个名字,正所谓不忘初码聚棧前行,总是希望在技术社区建设、程序员技能提升、技术价值变现等领域做点东西

而作为传统技术派的我,对stackoverflow那种简而美的架构甚是嶊崇对国内的技术商业应用现状也甚为不满,基于这样的理念和想法一直在悄咪咪的构建相关应用,既当做日常的技术训练也算是尋求梦想。

当然了更希望这样的应用有合理而完整的盈利模式,使用自动化技术在较少人力运营的情况下实现自驱动、自循环很快我會发布这个应用,有兴趣的小伙伴可以保持关注

3.03.想做一些自媒体工作

自媒体是近些年的热点,而自媒体本身也蕴含了个体价值和集体價值对立与共存的深刻思考,我也希望在不影响本体工作和责任的情况下建设一个自己的自媒体频道,分享见闻和感悟分享案例和经驗,在分享中结交新朋友在交流中学习新知识。

当然了我也不排斥自媒体本身能够获得收入,也希望收入能够反补分享带来更多更高质量的文章和资讯,希望本文是自媒体建设良好的第一篇章

3.04.想继续卖龙虾

去年的龙虾店又一次中止了,但任何行业的做事逻辑和方法嘟是相通的相信自己在不断成长的情况下,一定能再一次卖好龙虾没有人能够拒绝合理而客观的现金流收入,不是吗

好了,总结到這里就告一段落了我想说,越是经历了不同的磨难和历炼越能感受到时间是最大的毒药和解药,在这个复杂的世界里没有绝对的对錯与结论,有的只是沿途的风景和看风景的方式写下此文,既不为人师也不自我束缚,但愿总结和分享能给大家带来更多的思考

 
}

我要回帖

更多关于 产品密钥过期了之后会怎么办 的文章

更多推荐

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

点击添加站长微信