网狐棋牌框架内核通信设计原理剖析

                       

 

        关于网狐框架我使用和了解大致有5年多了,也深知里面的一些坑坑洼洼,其他人写的一些关于网狐框架分析的文章,大多都是贴上大量的代码,模块流程大致的解说,都是一些皮毛解说,并没有点出为什么这么做,还有通信机制更是无人谈及,以为就是几个简单函数的调用而已,如果不去理解,你看上100遍也是一无所获,就好比我们去看鲁讯的文章,文字你都看得懂,可等你去写文章的时候,你就是写不出那么热血沸腾,那么有感染力的文章。写程序也一样的,不去理解为什么,你去写别的程序时,很难将别人优秀的设计应用到你的程序设计当中去,鉴于以上原因,我决定献丑一下。

       额外插一句:我现在维护使用的是skynet框架进行棋牌游戏开发,我大量模仿网狐的棋牌框架设计,因为它实在是太好用了,或者说我还没有想到比它更加好的设计处理问题,这当中也让我更加明白网狐框架和其他棋牌游戏框架的优点和缺点,从而设计出更加现代化的棋牌框架。

        网狐游戏框架主要(如图)三大组件分为协调服务器,登陆服务器,游戏服务器,至于后来还有约战服务器,其实理解了前面三个,其他一样的,所以后面不会再谈及其他服务器。

   那么他们三个服务器之间采用什么通信呢?这个就得去看kernelEngine模块,从这里开始你才能说得通整个流程

这个模块搞清楚了,理解整个框架就能水到渠成,这个模块又会细分6大功能(如图)TimerEngine, TraceService, AttemperEngine, TCPNetworkEngine, AsynchronismEngine,DataBaseEngine

为什么需要TimerEngine,因为每个游戏,每个服务器都有各自的定时器,每个游戏几百个桌子,都各有各的桌子定时器,这个就是用来管理各个服务器之间的游戏定时器的触发。那TraceService又是干什么的呢?这个就是用来在widows窗口上打印一些相关信息用的,比如,包括游戏过程里面发生一些致命错误都可以在这里打印,来说到AttemperEngine这个调度引擎又是什么干什么用的呢?先看三个主要服务器,其实都定义了AttemperEngine的钩子类CAttemperEngineSink 所谓钩子就是一个回调处理模块,他会把自身的指针给到AttemperEngine,从而让AttemperEngine需要通知到CAttemperEngineSink时,得以调用。如图

有些人看到QUERY_OBJECT_INTERFACE就非常疑惑,这些就需要你去了解com编程的东西了,恰恰也是让这个框架能够得心应手地使用的功能。那什么时候AttemperEngine会通知到CAttemperEngineSink呢?

如上图所示,定时器触发通知,用户客户端连接,数据库消息通知,socket关闭,网络消息读取,等等都是通过这个回调指针调用处理,可见这个AttemperEngine承担了主要的通信处理和转发这就是为什么取名叫调度引擎。调度引擎里面还包含了TCPNetworkEngine的回调指针,如图

AttemperEngine拿到TCPNetworkEngine的指针来干嘛呢?因为AttemperEngine会对消息进行校验发现异常时,要通知网络关闭,主要都是这个作用。那这个异步引擎又是干什么用的呢?通过观察代码你会看到,有三个类定义了这个AsynchronismEngine的变量,分别是AttemperEngine,TCPNetworkEngine,DataBaseEngine

 

 

其实通过搜索你发现,这个类主要就是创建一个线程用来投递消息用的,如图

这时你会想为什么要通过这个异步引擎来进行消息投递,直接调用send发送不行吗?这个恐怕你仅看这个框架的代码你是难以明白的,这个时候就是关键的时候,你要打起十二分精神来看我往后的解析,通信引擎里面有有两个类,这两个类才是通信的核

心,CTCPSocketService这个类是用来处理各个服务器之间的通信的,TCPNetworkEngine是用来和客户端用户之间的通信的,同时TCPNetworkEngine还承担CTCPSocketService处理过过的消息的转发,这里面还应用了两种常见的windows下socket通信模式,你没有看错,同一个引擎里面应用了两种通信模式,CTCPSocketService应用的是WSAAsyncSelect异步I/O模型处理各个服务器之间的通信是非常好用的,CTCPSocketService应用的是完成端口模型,这个可是windows下特有的通信模型,支持高并发的模型,上面那个模型做不到,基本上windows下高并发模型都是用的这种模式,这里简单介绍一下完成端口模型通信的 特点,指定接收和发送消息的线程的数量为之服务,即所谓的线程池。这些线程是可复用的,怎么复用不用我们去关心,windows已经为我们做好了处理,这里不会展开解说,关于这两种通信模式的详细原理和解说,得推荐你看另外一本书《精通windows_socket网络开发:基于Visual_C++实现》(链接:链接:https://pan.baidu.com/s/1n6t1u9KM-XsG_n1yZgw3AA 
提取码:skva )其实我早几年前就看过网狐框架内核源码,可惜当时没有发现这本书,有些代码看得是丈二的和尚--摸不着头脑。相信我,你不看这本书你说你能理解得了这两种通信恐怕是不大可能的,我也是借助这本书至今才能完全理解。怎么印证上面我说的两大通信模型呢?下面我为你贴上代码一一解说一下,

先说一下WSAAsyncSelect异步I/O模型,

这种通信可以绑定具体的窗口实例通信,这是windows下的特色,那如何接收消息呢

上面图中GetMessage是关键,是通过循环不断获取消息,来读取你自己感兴趣的消息。那你会发现GetMessage为什么没有在循环体中,其实OnEventThreadRun这个是线程函数来的,会由系统自动循环调用的,这个框架里面所有的线程函数都是这样命名的,这样方便阅读和记忆。消息接发处理都在下面这图中的函数得以体现

接下来要说的是完成端口模型相关实战代码了。如下图

这里先生成完成端口,你会注意到SystemInfo.dwNumberOfProcessors这个玩意好像是要把计算机的核数传给他,这个是说你想用多少个线程来处理你的网络信息,现在这里是计算机有多少核,他就用多少个线程,也就是说你的服务器核数配高一点,那么你的网络处理能力就强一点,现在大家明白平时游戏网络卡,知道服务器怎么配置没有?

接下来就是为这个完成端口配置多少个线程了

接下来又调用一次         CreateIoCompletionPort((HANDLE)hConnectSocket, m_hCompletionPort, (ULONG_PTR)pTCPNetworkItem, 0);进行socket绑定和用什么结体体类型接收

那如何接收和发送消息呢,看下图

是通过GetQueuedCompletionStatus这个windows的函数来进去消息完成通知获取消息

OnAsynchronismEngineData这个函数来处理接收到的消息,

可以看到三个引擎都对这个回调接口进行了处理,所以m_pIAsynchronismEngineSink这个回调指针是谁的,那消息就是谁能收得到。

那发送消息怎么发送?你要发送的东西会先放到队列,由PostQueuedCompletionStatus进行线程通知

再由另外的线程提取发送。

       基本上到此,网络通信部分基本上你能理解他大体的处理方法了,至于怎么关闭socket,和线程退出你得结合前面介绍的那本书,和代码仔细琢磨就明白了。

      就拿一个游戏服务器来分析他是怎么将各个部件组织起来的,其他服务器原理是一样的,CServiceUnits类是关键,每个服务器都包含有这个类的,功能大体相同的,处理细节不同

这里创建了一大堆引擎的实例,引擎里面的回调指针全部在下面这个函数中完成传递InitializeService,

这些实例创建起来,各个引擎之间基本上是通过发送消息来沟通交流的如下图

,包括结果返回,也是消息形式返回处理,不会在这个类里面相互调用接口,那样太过混乱了,它最大的好处是:每个服务器都是这种形式处理消息,方便管理和维护,这一点也是我觉得它比较高明之处,每个服务器接收消息的接口都是相同的,不像我现在公司的skynet框架服务器,各种穿插调用,每个服务模块之间功能相同的,未必函数名相同,带来很大的记忆负担。

       看完我这篇文章你未必能明白各个流程细节之间的处理,你还是得大量调试,验证你心中所想,才能吸收其框架设计精妙之处。

欢迎有棋牌开发兴趣的伙伴一起交流学习,QQ:460000713.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章