online游戲服務器架構--數據庫及事件相關 .

Online服務器的第三部分就是數據層,send_request_to_db開始了數據層的處理邏輯:

int send_request_to_db(int cmd, sprite_t* p, int body_len, const void* body_buf, uint32_t id);

在該函數里首先以懶惰的方式連接數據庫服務器,獲取一個網絡連接,注意參數p,如果該參數為空,那么就說明不關心數據庫代理服務器返回的數據:

if (!p) pkg->seq = 0;

else pkg->seq = (sprite_fd (p) << 16) | p->waitcmd;

注意以上的代碼,如果不關心返回數據,那么直接將pkg的seq字段設置為0即可,如果關心返回結果,就需要用這個seq字段保存一些信息了,比如當前處理的業務協議是什么,還有就是這個客戶端實體p的對應的父進程的套結字描述符是多少,然后將這個pkg連同信息體一起發送給數據庫代理服務器,等到代理服務器返回的時候會進入worker_handle_net中處理,注意handle_process函數里關于子進程的執行路線:

int worker_handle_net(int fd, void* buf, int len, sprite_t** u)

{

assert( len >= sizeof(server_proto_t) );

server_proto_t* dbpkg = buf;

} else if (fd == proxysvr_fd) {

return handle_db_return(fd, dbpkg, len, u);

}

return 0;

}

執行流進入handle_db_return:

static int handle_db_return(int fd, server_proto_t* dbpkg, int len, sprite_t** sp)

{

int waitcmd = dbpkg->seq & 0xFFFF;

int conn = dbpkg->seq >> 16;

if (!dbpkg->seq || waitcmd == PROTO_LOGOUT) //如果不關心返回數據,則在send_request_to_db就已經將seq設置成了0,于是直接返回,否則取出保存的fd信息

return 0;

if (!(*sp = get_sprite_by_fd(conn)) || (*sp)->waitcmd != waitcmd) {

//出錯

}

int err = -1;

switch (dbpkg->ret) {

case 0:

break; //成功

…//處理各種錯誤碼

在處理各種錯誤碼的時候可以根據不同的協議進行不同的動作,協議保存在sprite_t的waitcmd字段中。在沒有錯誤的情況下就會進入數據層的回調處理:

#define DO_MESSAGE(n, func) /

case n: err = func(*sp, dbpkg->id, dbpkg->body, len - sizeof (server_proto_t)); break

和協議層的處理十分類似,也是回調函數的形式,只不過這里沒有提前注冊,只是簡單的封裝了一下switch-case開關。對于前面的例子就是:

DO_MESSAGE(SVR_PROTO_RACE_SIGN, race_sign_callback);

int race_sign_callback(sprite_t* p, uint32_t id, char* buf, int len)

{

uint32_t itms[2] = {12999, 12998};

CHECK_BODY_LEN(len, 4);

p->teaminfo.team = *(uint32_t*)buf;

if (p->teaminfo.team != 1 && p->teaminfo.team != 2) {

ERROR_RETURN(("race failed/t[%u %u]", p->id, p->teaminfo.team), -1);

}

db_single_item_op(0, p->id, itms[p->teaminfo.team - 1], 1, 1);

response_proto_uint32(p, p->waitcmd, p->teaminfo.team, 0); //一定要向客戶端回應,否則客戶端將掛起

return 0;

}

一定要返回給客戶端一個數據,因為客戶端和服務器是一問多答式的,服務器的應答可以分好幾部分來返回給客戶端,比如一共需要返回5次,那么在這5次全部返回之間,服務器是不接受同一個客戶端的別的請求的,必然是一問多答,而不是多問多答。注意send_to_self的最后一個參數的意義:

int send_to_self(sprite_t *p, uint8_t *buffer, int len, int completed)

如果completed為1,那么在該函數中就會將p的waitcmd設置為0,代表當前的這個協議已經處理完畢,online可以處理下一個協議請求了,否則就意味著當前的協議還沒有處理完畢,online不接收新的協議請求,這個在dispatch_protocol中體現:

if (p->waitcmd != 0) {

send_to_self_error(p, cmd, -ERR_system_busy, 0);

WARN_RETURN(("wait for cmd=%d, id=%u, new cmd=%d", p->waitcmd, p->id, cmd), 0);

}

Onlien服務器通過這種方式解決了一些同步問題,一條協議沒有處理完是不接受另外的協議的。關于數據同步,其實online服務器使用了另外的方案,并沒有使用傳統的鎖之類的,而是使用了一個全局變量,并且onlien中不存在線程的概念,因此基本不存在處理數據時的數據共享訪問,因此一個子進程同時只能處理一個客戶的請求,因此全局變量msg被定義出來,用來保存需要返回給客戶端的消息,注意包含協議頭部。最后的問題就是請求和回應時的數據組織了,對于請求包,用UNPKG_UINT32來解析包的內容,j是游標號,需要在外部定義然后在外部使用,初始值就是需要開始解析的位置距離包(也就是b)開始的以字節為單位的大小,比如一個buffer,協議頭為8個字節,我們需要解析協議體,也就是有效載荷,那么我們需要如下代碼:

Int j = 8, v = count;

UNPKG_UINT32(buffer, count, j);

只要看看下面的定義就一目了然了:

#define UNPKG_UINT32(b, v, j) /

do { /

(v) = ntohl( *(uint32_t*)((b)+(j)) ); (j) += 4; /

} while (0)

對于封包同樣的方式,只是將流程反過來了:

#define PKG_UINT32(b, v, j) /

do { /

*(uint32_t*)((b)+(j)) = htonl(v); (j) += 4; /

} while (0)

在往客戶端返回包的時候,封包的過程就是用的PKG_UINT32,如果連包頭一起封裝,那么就是下面的流程:

int j = sizeof(protocol_t); //空余了包頭的空間

PKG_UINT32(msg, intbuf1, j); //從包頭的下一個字節開始打包

PKG_UINT32(msg, intbuf2, j); //繼續打包

關于事件處理器是和數據庫相關的處理器并列的邏輯處理器,這個處理器主要處理系統事件的,由于事件分為好多種,如果寫進一個協議處理回調函數會使得這個函數的職責太多,不明確,如果每個事件作為一個協議封裝,那么又會使整個協議處理器的架構主次不分,很含糊,因此就專門為事件處理單獨列一個更低級的層次進行處理,也就是和協議處理不在一個層次,而專門為所有事件單獨封裝一個協議處理回調函數,然后為了協議處理的清晰,在這個協議處理鉤子中將事件分發到不同的事件處理器中,如此一來,事件處理就單獨成了一個子層次

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/444981.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/444981.shtml
英文地址,請注明出處:http://en.pswp.cn/news/444981.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

leecode5 最長回文子串

給定一個字符串 s&#xff0c;找到 s 中最長的回文子串。你可以假設 s 的最大長度為 1000。 示例 1&#xff1a; 輸入: "babad" 輸出: "bab" 注意: "aba" 也是一個有效答案。 示例 2&#xff1a; 輸入: "cbbd" 輸出: "bb"…

libxml解析xml文件的一些總結

libxml -- 解析 XML 文檔XML 介紹&#xff1a;XML 和 DOMlibxml 介紹 數據類型 — xmlChar數據結構 創建 XML 文檔解析 XML 文檔修改 xml 文檔Xpath — 處理大型 XML 文檔libxml2 庫函數要注意的函數讀取 xml 文件xml 操作基本結構及其指針類型根節點相關函數 創建子節點相關函…

Linux(7)-正則表達式

正則表達式demo1:在某個文件中尋找命令seddemo2:尋找8位電話號碼正則表達式&#xff1a;用來描述或者匹配某一系列符合某個句法隊則的字符串或者單個字符串。最初正則表達式&#xff0c;出現在自動控制理論和形式化語言理論中。 Linux 中 find grep sed ls命令都支持正則表達式…

服務器端開發的一些建議

摘要: 本文作為游戲服務器端開發的基本大綱&#xff0c;是游戲實踐開發中的總結。第一部分專業基礎&#xff0c;用于指導招聘和實習考核&#xff0c; 第二部分游戲入門&#xff0c;講述游戲服務器端開發的基本要點&#xff0c;第三部分服務端架構&#xff0c;介紹架構設計中的一…

leetcode63 不同路徑II

一個機器人位于一個 m x n 網格的左上角 &#xff08;起始點在下圖中標記為“Start” &#xff09;。 機器人每次只能向下或者向右移動一步。機器人試圖達到網格的右下角&#xff08;在下圖中標記為“Finish”&#xff09;。 現在考慮網格中有障礙物。那么從左上角到右下角將…

小談Online-game服務器端設計(1、2)

談這個話題之前&#xff0c;首先要讓大家知道&#xff0c;什么是服務器。在網絡游戲中&#xff0c;服務器所扮演的角色是同步&#xff0c;廣播和服務器主動的一些行為&#xff0c;比如說天氣&#xff0c;NPC AI之類的&#xff0c;之所以現在的很多網絡游戲服務器都需要負擔一些…

Linux(8)-Linux下的編程開發-C/C++、PHP、JAVA概述

Linux下的編程開發1.C/C語言開發環境的搭建2.PHP開發環境搭建3.JAVA開發環境搭建1.C/C語言開發環境的搭建 方式1:文本編輯器編譯器&#xff08;gcc/g&#xff09; Ubuntu 下常用的文本編輯器&#xff1a; Gedit–語法高亮Vim–vi(無比強大無比難用)的改進。字符界面/圖形界面…

leetcode55 跳躍游戲 秒殺所有答案

給定一個非負整數數組&#xff0c;你最初位于數組的第一個位置。 數組中的每個元素代表你在該位置可以跳躍的最大長度。 判斷你是否能夠到達最后一個位置。 示例 1: 輸入: [2,3,1,1,4] 輸出: true 解釋: 我們可以先跳 1 步&#xff0c;從位置 0 到達 位置 1, 然后再從位置 …

小談Online-game服務器端設計(3)

下面我想來談談關于服務器上NPC的設計以及NPC智能等一些方面涉及到的問題。首先&#xff0c;我們需要知道什么是NPC&#xff0c;NPC需要做什么。NPC的全稱是&#xff08;Non-Player Character&#xff09;&#xff0c;很顯然&#xff0c;他是一個character&#xff0c;但不是玩…

小談Online-game服務器端設計(4)

在這一章節&#xff0c;我想談談關于服務器端的腳本的相關設計。因為在上一章節里面&#xff0c;談NPC智能相關的時候已經接觸到一些腳本相關的東東了。還是先來談談腳本的作用吧。   在基于編譯的服務器端程序中&#xff0c;是無法在程序的運行過程中構建一些東西的&#xf…

leetcode45 跳躍游戲II 秒殺所有答案

給定一個非負整數數組&#xff0c;你最初位于數組的第一個位置。 數組中的每個元素代表你在該位置可以跳躍的最大長度。 你的目標是使用最少的跳躍次數到達數組的最后一個位置。 示例: 輸入: [2,3,1,1,4] 輸出: 2 解釋: 跳到最后一個位置的最小跳躍數是 2。 從下標為 …

MachineLearning(7)-決策樹基礎+sklearn.DecisionTreeClassifier簡單實踐

sklearn.DecisionTreeClassifier決策樹簡單使用1.決策樹算法基礎2.sklearn.DecisionTreeClassifier簡單實踐2.1 決策樹類2.3 決策樹構建2.3.1全數據集擬合&#xff0c;決策樹可視化2.3.2交叉驗證實驗2.3.3超參數搜索2.3.4模型保存與導入2.3.5固定隨機數種子參考資料1.決策樹算法…

游戲服務器體系結構

本文描述了一個我所設計的游戲服務器體系結構,其目的是實現游戲服務器的動態負載平衡,將對象從繁忙的服務器轉移到相對空閑的服務器中.設計并沒有經過具體的測試與驗證,僅僅是將自己目前的一些想法記錄下來.隨著新構思的出現,可能會有所變化. 以下是服務器的邏輯視圖,其中忽略…

游戲服務器架構探討

要描述一項技術或是一個行業&#xff0c;一般都會從其最古老的歷史開始說起&#xff0c;我本也想按著這個套路走&#xff0c;無奈本人乃一八零后小輩&#xff0c;沒有經歷過那些苦澀的卻令人羨慕的單機游戲開發&#xff0c;也沒有響當當的拿的出手的優秀作品&#xff0c;所以也…

leetcode72 編輯距離

給定兩個單詞 word1 和 word2&#xff0c;計算出將 word1 轉換成 word2 所使用的最少操作數 。 你可以對一個單詞進行如下三種操作&#xff1a; 插入一個字符 刪除一個字符 替換一個字符 示例 1: 輸入: word1 "horse", word2 "ros" 輸出: 3 解釋: ho…

即時通訊系統架構

有過幾款IM系統開發經歷&#xff0c;目前有一款還在線上跑著。準備簡單地介紹一下大型商業應用的IM系統的架構。設計這種架構比較重要的一點是低耦合&#xff0c;把整個系統設計成多個相互分離的子系統。我把整個系統分成下面幾個部分&#xff1a;&#xff08;1&#xff09;狀態…

leetcode303 區域和檢索

給定一個整數數組 nums&#xff0c;求出數組從索引 i 到 j (i ≤ j) 范圍內元素的總和&#xff0c;包含 i, j 兩點。 示例&#xff1a; 給定 nums [-2, 0, 3, -5, 2, -1]&#xff0c;求和函數為 sumRange() sumRange(0, 2) -> 1 sumRange(2, 5) -> -1 sumRange(0,…

算法(24)-股票買賣

股票買賣1.動態規劃框架LeetCode-121 一次買賣LeetCode-122 不限次數LeetCode-309 不限次數冷凍期LeetCode-714 不限次數手續費LeetCode-123 兩次買賣LeetCode-188 k次買賣2.貪心特解LeetCode-121 一次買賣LeetCode-122 不限次數解題思路參考buladong解題&#xff0c;詳細信息可…

網絡游戲的客戶端同步問題 .

有關位置同步的方案實際上已經比較成熟&#xff0c;網上也有比較多的資料可供參考。在《帶寬限制下的視覺實體屬性傳播》一文中&#xff0c;作者也簡單提到了位置同步方案的構造過程&#xff0c;但涉及到細節的地方沒有深入&#xff0c;這里專門針對這一主題做些回顧。 最直接的…

leetcode319 燈泡的開關

初始時有 n 個燈泡關閉。 第 1 輪&#xff0c;你打開所有的燈泡。 第 2 輪&#xff0c;每兩個燈泡你關閉一次。 第 3 輪&#xff0c;每三個燈泡切換一次開關&#xff08;如果關閉則開啟&#xff0c;如果開啟則關閉&#xff09;。第 i 輪&#xff0c;每 i 個燈泡切換一次開關。 …