NetHost指針訪問安全專集(基於mdk1.10)

NetHost是服務器引擎的2大接口類之一,並且是由引擎維護其對象的建立與釋放, 安全

用戶使用不當可能形成訪問野指針或者內存泄露。 服務器

爲了讓使用mdk的朋友,安全的使用NetHost指針,本文寫提出一些安全的使用模型。

首先說明一下,若是擔憂NetHost使用不當,能夠徹底放棄NetHost,引擎的另一大接口類NetServer已經提供了SendMsg()與CloseConnect()方法,用於安全的操做主機的鏈接,只要傳遞HostID進去(能夠經過NetHost->ID()方法獲得)便可,ID只是一個int值,不存在內存泄露與野指針問題。

接收數據,在OnMsg中使用參數的NetHost指針是絕對安全的,引擎已經作好控制。


開始NetHost的安全訪問 併發

NetHost表示一個鏈接上來的主機,該類主要就是2個做用send發消息close斷開鏈接,另外提供方便的Group操做 spa


爲了不方法野指針和內存泄露,主要要了解NetHost對象的生存週期
NetHost對象在鏈接創建時被建立
鏈接斷開(不論是server斷開,仍是client斷開)後被移動到關閉列表(代碼中就是m_closedConnects裏面),
後臺有一個釋放線程,會循環定時去檢查這個列表中的指針,若是對象的訪問計數爲0,則釋放掉對象

對象什麼時是絕對安全的(不會被釋放)
1.被做爲參數傳遞給OnConnect OnMsg OnClose時,參數中的NetHost指針是絕對不會被引擎釋放的,
由於引擎在調用這3個方法前已經將對象的方法計數+1,在退出後會將訪問計數-1,也就是說這3個方法一退出,對象就有可能被釋放。
2.OnClose被調用前,NetHost對象絕對不會被引擎釋放,也就是說OnConnect OnMsg退出後,指針依舊是安全的,
但這不表示,你在OnConnect中將指針複製之後,下次在OnMsg中使用就是安全的,由於OnConnect OnMsg OnClose是併發的

爲了避免讓底層釋放對象,用戶可使用惟一的方法——使用Hold()來將訪問計數+1,
用完之後記得使用Free()來將訪問計數-1,好讓引擎最終有機會釋放對象,不然引用計數沒法歸0,引擎永遠不會釋放對象,這就是內存泄露

下面舉例說明一些須要Hold Free的狀況
做過服務器開發的應該很容易想到,一個鏈接創建了在OnConnect中就要保存表示這個鏈接的對象,
未來在OnMsg或其它業務中,用這個對象來通知其該玩家,沒錯服務器對於鏈接的使用就是這樣,沒有其它花樣。

那麼咱們創建一個map<int,NetHost*> list用於保存全部鏈接
好比有3個玩家abc鏈接進來
onconnect(pPlayer)
步驟1.lock list
步驟2.pPlayer->Hold()訪問計數+1
步驟3.將指針保存到list  //abc到了list裏面,而且訪問計數=1,即便鏈接斷開,在用戶調用Free()以前,引擎不會釋放他們
步驟4.unlock

onclose(pPlayer)
步驟1.lock list
步驟2.將指針從list刪除
步驟3.unlock
步驟4.pPlayer->Free()不在使用了,訪問計數-1

好比玩家a攻擊了玩家b
onmsg(pPlayer)
步驟1.lock list
步驟2.在list中找到玩家b的NetHost指針賦值給pPlayerB
步驟3.unlock
步驟4.pPlayerB->Send()通知玩家B,受到玩家A的攻擊

如今開始作狀況分析
if步驟4以前,玩家B退出遊戲,而且發生線程切換,進入onclose()
結果就是pPlayerB指向的對象訪問計數歸0,引擎得到了該對象的釋放權利(雖然不必定會馬上釋放,由於是定時去檢查要不要釋放)。
但這就已經不安全了,服務器7*24小時長期運行,並且幾千用戶鏈接斷開,要碰到正好引擎把指針給釋放了,概率很是高。
結果就是線程切換回onmsg執行步驟4時,訪問一個野指針,服務器崩潰。

就是說在步驟4.以前要pPlayer->Hold()1下,讓訪問計數變爲2,
萬一這時B退出,訪問計數仍是1,引擎不會釋放對象,
步驟4以後,再pPlayer->Free()下,若是B退出了就是0,沒退出就1,正常

這樣就沒問題了?
不對pPlayer->Hold()和pPlayerB->Send()沒有區別都是在unlock以外使用pPlayerB這個指針,一樣會被線程切換打斷,致使訪問野指針
因此pPlayer->Hold()必須放在lock unlock內。這樣就沒問題了

總結一下就是,指針進入列表,從列表取出來使用,都要在lock unlock以內完成hold,由於hold在lock以內,能夠阻止釋放線程先行free,由於free要在從列表刪除以後,也就是lock以後(線程會被掛起),而free能夠在unlock前(互斥區內),也能夠在unlock以後(互斥區外)進行,是安全的。

這樣的話,若是OnMsg步驟1lock以前,玩家B退出遊戲,發生線程切換,OnClose中完成free,則線程再次切換回OnMsg時候list中已經取不到玩家B的對象了,表示玩家已退出,一切正常。


最後OnMsg中不Hold Free可不能夠?
答:能夠,將步驟4放到lock以內就好了

這樣最簡單,
onconnect()時lock hold,進列表
onclose()時lock從列表刪除unlock free
要用的時候lock,要麼對象已經被從列表中刪除,要麼阻止onclose線程,用完unlock,沒必要hold free
相關文章
相關標籤/搜索