網狐棋牌框架內核通信設計原理剖析

                       

 

        關於網狐框架我使用和了解大致有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.

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