freepeople性欧美熟妇, 色戒完整版无删减158分钟hd, 无码精品国产vα在线观看DVD, 丰满少妇伦精品无码专区在线观看,艾栗栗与纹身男宾馆3p50分钟,国产AV片在线观看,黑人与美女高潮,18岁女RAPPERDISSSUBS,国产手机在机看影片

正文內(nèi)容

游戲服務(wù)器架構(gòu)(參考版)

2024-10-13 20:02本頁面
  

【正文】 其實(shí)每一個(gè)游戲的策劃都要經(jīng)歷很多步驟和過程,就游戲策劃的前期準(zhǔn)備工作當(dāng)中就包含了對(duì)技術(shù)、經(jīng)濟(jì)、人力資源這三點(diǎn)的可行性分析,市場(chǎng)調(diào)研,確定工作計(jì)劃以及撰寫策劃草案這四個(gè)步驟,下面我將對(duì)其一一經(jīng)行分析。在這篇文章當(dāng)中我將針對(duì)游戲策劃當(dāng)中的前期準(zhǔn)備進(jìn)行說明,并發(fā)表自己的看法。不管是怎樣一種方式,了解了其過程,代碼實(shí)現(xiàn)都是比較簡單的,我們就不再贅述了第四篇:游戲架構(gòu)設(shè)計(jì)淺談?dòng)螒虿邉澋那捌诠ぷ靼嗉?jí):09數(shù)字媒體技術(shù)姓名:廖偉民學(xué)號(hào):090804006摘要:游戲制作所涉及的知識(shí)領(lǐng)域極其廣泛,其中就單其游戲策劃這一塊就涉及到三大內(nèi)容:前期的準(zhǔn)備工作、中期的制作工作、后期的宣傳工作。但是,很顯然的,wow并沒有采用這種方案,因?yàn)榭蛻舳嗽谶x擇世界服時(shí)并沒有向服務(wù)器發(fā)送要求確認(rèn)的消息。當(dāng)然了,為了session key的安全,所有的服務(wù)器在收到一個(gè)新的session key后都會(huì)為其設(shè)一個(gè)有效期,在有效期過后還沒來認(rèn)證的,則該session key會(huì)被自動(dòng)刪除。如果客戶端這時(shí)想要去某個(gè)游戲世界,那么他必須先通知當(dāng)前連接的LoginServer要去的服務(wù)器地址,LoginServer將session key安全轉(zhuǎn)移給目標(biāo)服務(wù)器,轉(zhuǎn)移的意思是要確保目標(biāo)服務(wù)器收到了session key,本地保存的要?jiǎng)h除掉。一個(gè)可行的方案是,讓任意時(shí)刻只有一個(gè)地方保存一個(gè)客戶端的session key,這個(gè)地方可能是客戶端當(dāng)前正連接著的服務(wù)器,也可以是它正要去連接的服務(wù)器。所以,我們也可以試著考慮一種新的方案,一種不需要去全區(qū)唯一一個(gè)入口查詢的方案。這兩種方案的本質(zhì)并無差別,只是看你愿意將負(fù)載放在哪里。那么,LoginServer將這個(gè)key存到數(shù)據(jù)庫中,讓網(wǎng)關(guān)服自己去數(shù)據(jù)庫驗(yàn)證?似乎也是個(gè)可行的方案。方法很簡單,去找給他登機(jī)牌的那個(gè)檢票員問一下,這張牌是不是他發(fā)的不就得了。一樣的處理過程,我們的登錄服在驗(yàn)明客戶端身份后,也會(huì)發(fā)給客戶端一個(gè)登機(jī)牌,這個(gè)登機(jī)牌還有一個(gè)學(xué)名,叫做session key。打個(gè)比方,有個(gè)不自覺的玩家不遵守游戲規(guī)則,沒有去驗(yàn)證帳號(hào)密碼就直接跑去連接世界服了,就如同一個(gè)不自覺的乘客沒有換登機(jī)牌就直接跑到登機(jī)口一樣。這是一個(gè)很必要的設(shè)計(jì),保證了我們?cè)谝蛞馔馇闆r連接不上世界服或者發(fā)現(xiàn)世界服正在排隊(duì)而想換另外一個(gè)試試時(shí)不會(huì)需要重新進(jìn)行密碼驗(yàn)證。當(dāng)客戶端選擇了一個(gè)世界之后該怎么辦?wow的做法是,當(dāng)客戶端選擇一個(gè)游戲世界時(shí),客戶端會(huì)主動(dòng)去連接該世界服的IP和PORT,然后進(jìn)入這個(gè)游戲世界。如果客戶端一直連接著,則該狀態(tài)會(huì)以每5秒一次的頻率不停刷新列表給客戶端,當(dāng)然是否值得這樣做還是有待商榷。升級(jí)狀態(tài)其實(shí)就是文件傳輸過程,文件發(fā)送完畢后通知客戶端開始執(zhí)行升級(jí)文件并關(guān)閉連接。密碼驗(yàn)證的過程使用了SRP6協(xié)議,具體過程就不多做描述,每個(gè)游戲使用的方式也都不大一樣。先從狀態(tài)機(jī)開始,前面也說過了,登錄服上的連接會(huì)有兩種狀態(tài),一是帳號(hào)密碼驗(yàn)證狀態(tài),一是服務(wù)器列表選擇狀態(tài),其實(shí)還有另外一個(gè)狀態(tài)我們未曾討論過,因?yàn)樗c我們的登錄過程并無多大關(guān)系,這就是升級(jí)包發(fā)送狀態(tài)。WorldServerMgr實(shí)現(xiàn)所要考慮的內(nèi)容就這些,我們?cè)賮砜纯碙oginServer,這才是我們今天要重點(diǎn)討論的對(duì)象。由于世界列表并不常變化,所以LoginServer沒有必要每次發(fā)送世界列表時(shí)都到WorldServerMgr上去取,LoginServer完全可以自己維護(hù)一個(gè)列表,當(dāng)WorldServerMgr上的列表發(fā)生變化時(shí),WorldServerMgr會(huì)主動(dòng)通知所有的LoginServer也更新一下自己的列表。這里可以用一個(gè)心跳包來實(shí)現(xiàn)其狀態(tài)的檢測(cè),如果WorldServer的連接斷開或者在規(guī)定時(shí)間內(nèi)未收到心跳包,則將其狀態(tài)更新為離線。類似的,如果DNSServer也可以讓LoginServer自己去注冊(cè),這樣在臨時(shí)LoginServer時(shí)就不需要去改動(dòng)DNSServer的配置文件了。從上面的過程描述中,我們很容易想到利用一個(gè)臨時(shí)的列表來保存世界服信息,這也是我們?cè)黾覹orldServerMgr服務(wù)器的目的所在。剛開始時(shí),世界列表是空的,慢慢的,世界服會(huì)一個(gè)個(gè)加入進(jìn)來,而這里如果有世界服當(dāng)機(jī),他會(huì)顯示為離線,不會(huì)從列表中刪除。在mangos的代碼中,我們注意到登錄服是從數(shù)據(jù)庫中取的世界列表,而在wow官方服務(wù)器中,我們卻會(huì)注意到,這個(gè)世界服列表并不是一開始就固定,而是動(dòng)態(tài)生成的。LoginServer處理玩家的登錄及世界服選擇請(qǐng)求。GateWay/WorldServer GateWay/WodlServer LoginServer LoginServer DNSServer WorldServerMgr | | | | | | | | internet | clients其中DNSServer負(fù)責(zé)帶負(fù)載均衡的域名解析服務(wù),返回LoginServer的IP地址給客戶端。再談登錄服的實(shí)現(xiàn)離我們的登錄服實(shí)現(xiàn)已經(jīng)太遠(yuǎn)了,先拉回來一下。在上面的文字中就同時(shí)出現(xiàn)了消息、事件和信號(hào)三個(gè)相近的概念,而在實(shí)際處理中,經(jīng)常發(fā)現(xiàn)三者不知道如何界定的情況,實(shí)際的情況比我在這里描述的要混亂的多。我們需要注意的一個(gè)很重要的問題是會(huì)不會(huì)引起循環(huán)調(diào)用。而信號(hào)的處理方式有些不同,信號(hào)處理是立即回調(diào)的,也就是一個(gè)信號(hào)產(chǎn)生后,他上面所注冊(cè)的所有槽都會(huì)立即被回調(diào)。最后需要我們考慮的是事件和信號(hào)的處理方式。對(duì)于信號(hào)的處理則比較簡單,默認(rèn)是沒有順序的,如果需要明確的順序,可以在信號(hào)注冊(cè)時(shí)顯示地指明槽的位置。另外還有一個(gè)需要我們關(guān)注的問題是事件和信號(hào)處理時(shí)的優(yōu)先級(jí)問題。關(guān)于事件和信號(hào)機(jī)制的實(shí)現(xiàn),網(wǎng)絡(luò)上的開源訓(xùn)也比較多,比如FastDelegate,sigslot,boost::signal等,其中sigslot還被Google采用,在libjingle的代碼中我們可以看到他是如何被使用的。簡單點(diǎn)說,就是我們可以為事件定義過濾器,使得事件可以被過濾。但有一項(xiàng)區(qū)別在于,事件處理函數(shù)的返回值是有意義的,我們要根據(jù)這個(gè)返回值來確定是否還要繼續(xù)事件的處理,比如在QT中,事件處理函數(shù)如果返回true,則這個(gè)事件處理已完成,QApplication會(huì)接著處理下一個(gè)事件,而如果返回false,那么事件分派函數(shù)會(huì)繼續(xù)向上尋找下一個(gè)可以處理該事件的注冊(cè)方法。這樣,與QT類似,對(duì)于事件我們可以重定義其處理方法,甚至過濾掉某些事件使其不被處理,但對(duì)于信號(hào)我們只是收到了一個(gè)通知,有些類似于Observe模式中的觀察者,當(dāng)收到更新通知時(shí),我們只能更新自己的狀態(tài),對(duì)剛剛發(fā)生的事件我不已不能做任何影響。比如在QT應(yīng)用程序中,用戶的一次鼠標(biāo)點(diǎn)擊會(huì)產(chǎn)生一個(gè)鼠標(biāo)點(diǎn)擊事件加入到事件隊(duì)列中,當(dāng)處理此事件時(shí)可能會(huì)導(dǎo)致某個(gè)按鈕控件產(chǎn)生一個(gè)clicked()信號(hào)。本節(jié)內(nèi)容欠考慮,希望大家多給點(diǎn)意見。比如在游戲服務(wù)器上玩家的狀態(tài)管理,還有在實(shí)現(xiàn)NPC人工智能時(shí)的各種狀態(tài)管理,這些就留作以后的專題吧。BR正如同在設(shè)計(jì)模式中所描述的,所有的模式都是已有問題的另一種解決方案,也就是說這并不是唯一的解決方案。當(dāng)我們?cè)谶M(jìn)行狀態(tài)轉(zhuǎn)換時(shí),可能會(huì)需要將一些現(xiàn)場(chǎng)數(shù)據(jù)從老狀態(tài)對(duì)象轉(zhuǎn)移到新狀態(tài)對(duì)象,這需要在定義接口時(shí)做一下考慮。具體的實(shí)現(xiàn)細(xì)節(jié)就不做過多描述了。當(dāng)要進(jìn)行狀態(tài)轉(zhuǎn)換時(shí),調(diào)用MachineBase的ChangeState()方法,顯示地告訴狀態(tài)機(jī)管理類自己要轉(zhuǎn)到哪一個(gè)狀態(tài)。}。狀態(tài)機(jī)基類接口:MachineBase{void ChangeState(StateBase* state)= 0。void Process(Message* msg)= 0。稍作整理,大概的代碼會(huì)類似這樣:狀態(tài)基類接口:StateBase{void Enter()= 0。由于這里的兩個(gè)狀態(tài)標(biāo)識(shí)只區(qū)分出了兩種狀態(tài),所以,我們僅需要兩個(gè)獨(dú)立的類,用以表示兩種狀態(tài)即可。描述的情況與我們這里所要處理的情況是如此的相似,也許我們可以試一試。而這個(gè)狀態(tài)標(biāo)識(shí)的改變是在運(yùn)行時(shí)進(jìn)行的,確切的說是在收到某個(gè)消息并正確處理完后改變的。該結(jié)構(gòu)體定義了每個(gè)消息碼的處理函數(shù)及需要的狀態(tài)標(biāo)識(shí),只有當(dāng)前狀態(tài)滿足要求時(shí)才會(huì)調(diào)用指定的處理函數(shù),否則這個(gè)消息碼的出現(xiàn)是不合法的。bool(AuthSocket::*handler)(void)。首先還是從mangos的代碼開始看起,我們注意到登錄服在處理客戶端發(fā)來的消息時(shí)用到了這樣一個(gè)結(jié)構(gòu)體:struct AuthHandler{eAuthCmd cmd。有關(guān)該優(yōu)化的說明在云風(fēng)描述其連接服務(wù)器實(shí)現(xiàn)的blog文章中也有講到,有興趣的可以去閱讀一下。采用第二種方式還可以附帶一個(gè)優(yōu)化方案??紤]數(shù)據(jù)緩存的話,那這里這可以有兩種實(shí)現(xiàn)方式了,一是為每個(gè)玩家準(zhǔn)備一個(gè)緩沖區(qū),另外就是只有一個(gè)全局的緩沖區(qū),要發(fā)送的數(shù)據(jù)加入到全局緩沖區(qū)的時(shí)候同時(shí)要指明這個(gè)數(shù)據(jù)是發(fā)到哪個(gè)socket的。但是直接send調(diào)用有時(shí)候有會(huì)存在一些問題,比如遇到系統(tǒng)的發(fā)送緩沖區(qū)滿而阻塞住的情況,或者只發(fā)送了一部分?jǐn)?shù)據(jù)的情況也時(shí)有發(fā)生。服務(wù)器公共組件實(shí)現(xiàn)發(fā)包的方式前面一直都在說接收數(shù)據(jù)時(shí)的處理方法,我們應(yīng)該用專門的IO線程,接收到完整的消息包后加入到主線程的消息隊(duì)列,但是主線程如何發(fā)送數(shù)據(jù)還沒有探討過。因?yàn)槲覀儗?duì)同一個(gè)隊(duì)列不再會(huì)出現(xiàn)同時(shí)讀和寫的情況,每個(gè)隊(duì)列在寫滿后交給邏輯線程去讀,邏輯線程讀完后清空隊(duì)列再交給IO線程去寫,一段固定大小的緩沖區(qū)即可。在解包及解密完成后,我們會(huì)將這個(gè)數(shù)據(jù)包復(fù)制到邏輯線程消息隊(duì)列中,如果我們只使用一個(gè)隊(duì)列,那這里也將會(huì)是個(gè)環(huán)形緩沖區(qū),IO線程往里寫,邏輯線程在后面讀,互相追逐。環(huán)形緩沖區(qū)是一項(xiàng)很好的技術(shù),不用頻繁的分配內(nèi)存,而且在大多數(shù)情況下,內(nèi)存的反復(fù)使用也使得我們能用更少的內(nèi)存塊做更多的事。也就是,大家都得遵守規(guī)定,追的人不能從桌子上跨過去,跑的人當(dāng)然也不允許反過來跑。要是一直這么反著追,估計(jì)您就只能換一個(gè)跑的更快的追逐者了,要不這游戲還真沒法玩下去。如果追上了怎么辦?那就是沒有數(shù)據(jù)可讀了,先等會(huì)兒唄,等跑的人向前跑幾步了再追,總不能讓游戲沒得玩了吧。但是對(duì)于這種按照嚴(yán)格的先進(jìn)先出順序處理的,塊大小并不算小的,而且塊大小也并不統(tǒng)一的內(nèi)存分配情況來說,更多使用的是一種叫做環(huán)形緩沖區(qū)的方案,mangos的網(wǎng)絡(luò)代碼中也有這么一個(gè)東西,其原理也是比較簡單的。頻繁的內(nèi)存分配不但增加了系統(tǒng)開銷,更使得內(nèi)存碎片不斷增多,非常不利于我們的服務(wù)器長期穩(wěn)定運(yùn)行。arthur的方案很好的解決了上一個(gè)方案遺留的問題,但因?yàn)橹挥幸粋€(gè)寫隊(duì)列,所以當(dāng)想要提供多個(gè)IO線程時(shí),線程間互斥地寫入數(shù)據(jù)可能會(huì)增大競爭的機(jī)會(huì),當(dāng)然,如果只有一個(gè)IO線程那將是非常完美的。兩種方案都是很優(yōu)秀的優(yōu)化方案,但也都是有其適用范圍的。雖然看起來鎖的調(diào)用次數(shù)是比前一種方案要多很多,但實(shí)際上大部分鎖調(diào)用都是不會(huì)引起阻塞的,只有在邏輯線程調(diào)換隊(duì)列的那一瞬間可能會(huì)使得某個(gè)線程阻塞一下。兩個(gè)隊(duì)列,一個(gè)給邏輯線程讀,一個(gè)給IO線程用來寫,當(dāng)邏輯線程讀完隊(duì)列后會(huì)將自己的隊(duì)列與IO線程的隊(duì)列相調(diào)換。Ghost Cheng在他的描述中沒有講到如何解決這種問題,但我們可以先來看看另一個(gè)方案。這里為每個(gè)隊(duì)列設(shè)了個(gè)最大消息數(shù),看來好像是打算只有當(dāng)IO線程寫滿隊(duì)列時(shí)才會(huì)將其放回到容器中換另一個(gè)隊(duì)列。而邏輯線程取消息時(shí)是從隊(duì)列容器中取一個(gè)有消息的隊(duì)列來讀取,處理完后清空隊(duì)列再放回到容器中。提供一個(gè)隊(duì)列容器,里面有多個(gè)隊(duì)列,每個(gè)隊(duì)列都可固定存放一定數(shù)量的消息。其最大的問題在頻繁的鎖競爭上。我們所能想到的最簡單的消息隊(duì)列可能就是使用stl的list來實(shí)現(xiàn)了,即消息隊(duì)列內(nèi)部維護(hù)一個(gè)list和一個(gè)互斥鎖,putMessage時(shí)將message加入到隊(duì)列尾,getMessage時(shí)從隊(duì)列頭取一個(gè)message返回,同時(shí)在getMessage和putMessage之前都要求先獲取鎖資源。有關(guān)消息隊(duì)列的實(shí)現(xiàn)和線程間消息的傳遞在ACE中有比較完全的代碼實(shí)現(xiàn)及描述,還有一些使用示例,是個(gè)很好的參考。Message* getMessage()。好比是,我這里提供了一個(gè)倉庫,有很多的供貨商,他們有貨要給我的時(shí)候只需要交到倉庫,然后我再到倉庫去取就是了,這個(gè)倉庫也就是消息隊(duì)列。}這里就有一個(gè)問題需要探討了,在getMessage()的時(shí)候,我們應(yīng)該去哪里取消息?前面我們考慮過,至少會(huì)有三個(gè)消息來源,而我們還討論過,這些消息源的IO操作都是在獨(dú)立的線程中進(jìn)行的,我們這里的主線程不應(yīng)該直接去那幾處消息源進(jìn)行阻塞式的IO操作。我們可以用一段簡單的偽碼來描述這個(gè)循環(huán)過程:while(Message* msg = getMessage()){if(msg為服務(wù)器關(guān)閉消息)break。這個(gè)循環(huán)將一直持續(xù),直到收到一個(gè)通知服務(wù)器關(guān)閉的消息包。另外對(duì)于大多數(shù)服務(wù)器程序來說,在運(yùn)行時(shí)都是作為精靈進(jìn)程或服務(wù)進(jìn)程的,所以我們并不需要服務(wù)器能夠處理控制臺(tái)用戶輸入,我們所要處理的數(shù)據(jù)來源都來自網(wǎng)絡(luò)。正如我們?cè)懻撨^的,為了游戲主邏輯循環(huán)的流暢運(yùn)行,所有比較耗時(shí)的IO操作都會(huì)分享到單獨(dú)的線程中去做,如網(wǎng)絡(luò)IO,數(shù)據(jù)庫IO和日志IO等。想象一下,因某個(gè)玩家上線而發(fā)起的一次數(shù)據(jù)庫查詢操作導(dǎo)致服務(wù)器內(nèi)所有在線玩家都卡住不動(dòng)將是多么恐怖的一件事!另外還有一些如事件、腳本、消息隊(duì)列、狀態(tài)機(jī)、日志和異常處理等公共組件,我們也會(huì)在接下來的時(shí)間里進(jìn)行探討。另外,在服務(wù)器實(shí)現(xiàn)上,網(wǎng)絡(luò)IO與邏輯處理一般會(huì)放在不同的線程中,以免耗時(shí)較長的IO過程阻塞住了需要立即反應(yīng)的游戲邏輯。如果我們使用windows平臺(tái),那IOCP將是首選;如果是linux,epool將是不二選擇。很簡單的結(jié)構(gòu),也比較容易理解。所以,mangos登錄服主循環(huán)的邏輯,也包括后面游戲服的邏輯,主循環(huán)的關(guān)鍵代碼其實(shí)是在SocketHandler中,也就是那個(gè)Select函數(shù)中。mangos的登錄服是一個(gè)單線程的結(jié)構(gòu),雖然在數(shù)據(jù)庫連接中可以開啟一個(gè)獨(dú)立的線程,但這個(gè)線程也只是對(duì)無返回結(jié)果的執(zhí)行類SQL做緩沖,而對(duì)需要有返回結(jié)果的查詢類SQL還是在主邏輯線程中阻塞調(diào)用的。由于我在讀技術(shù)文章時(shí)最不喜看到的就是大段大段的代碼,特別是那些直接Ctrl+C再Ctrl+V后未做任何修改的代碼,用句時(shí)髦的話說,一點(diǎn)技術(shù)含量都沒
點(diǎn)擊復(fù)制文檔內(nèi)容
環(huán)評(píng)公示相關(guān)推薦
文庫吧 www.dybbs8.com
備案圖鄂ICP備17016276號(hào)-1