• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            教父的告白
            一切都是紙老虎
            posts - 82,  comments - 7,  trackbacks - 0
            作者博客:
            http://blog.csdn.net/yahle
            大綱:
            項(xiàng)目的歷史背景
            服務(wù)器的設(shè)計(jì)思路
            服務(wù)器的技術(shù)
            服務(wù)器的設(shè)計(jì)
            服務(wù)器的改進(jìn)
            圖形引擎myhoho及UI庫的設(shè)計(jì)

            客戶端與服務(wù)器的集成


            網(wǎng) 絡(luò)游戲一般采用C\S模式,網(wǎng)絡(luò)游戲的設(shè)計(jì)重點(diǎn),我認(rèn)為在于Server端,也就是我們說的服務(wù)器。在服務(wù)器端的設(shè)計(jì),我把服務(wù)器按照功能分為2個(gè)部分, 一個(gè)負(fù)責(zé)游戲世界的處理,一個(gè)服務(wù)器服務(wù)器與客戶端的通訊。在負(fù)責(zé)游戲世界的處理的服務(wù)器,我又按照功能分為地圖服務(wù)器和邏輯服務(wù)器。這樣劃分的依據(jù)是他 們處理的內(nèi)容不同進(jìn)行。當(dāng)初的設(shè)計(jì)還考慮到系統(tǒng)的集群功能,可以把游戲的地圖移動(dòng)處理和游戲的邏輯處理都分別分?jǐn)偟狡渌?wù)器里面去。但是做到最后,發(fā)現(xiàn) 這樣的設(shè)計(jì)也不是太好,主要是因?yàn)樵谔幚硪恍┯螒蚴录臅r(shí)候需要兩個(gè)服務(wù)器之間進(jìn)行協(xié)同,這樣勢必要?jiǎng)?chuàng)建一定的網(wǎng)絡(luò)游戲消息,在開始制作游戲的時(shí)候,因?yàn)? 需要系統(tǒng)的東西不是很多,所以沒有太注意,到項(xiàng)目的后期,想增加一個(gè)功能的時(shí)候,就發(fā)現(xiàn)在處理船只沉沒的時(shí)候,服務(wù)器需要傳遞很多同步數(shù)據(jù),而且服務(wù)器各 自在設(shè)置玩家數(shù)據(jù)的時(shí)候,也有很多重復(fù)的地方。如果今后還要再加點(diǎn)什么其它功能,那要同步的地方就實(shí)在是太多了,所以按照功能把服務(wù)器分為2個(gè)部分的設(shè)計(jì) 還是存在缺陷的,如果讓我重新再來,我會(huì)選擇單服務(wù)器的設(shè)計(jì),當(dāng)然這個(gè)服務(wù)器還是要和連接服務(wù)器進(jìn)行分離,因?yàn)橛螒虻倪壿嬏幚砗团c玩家的通訊還是很好分開 的,而且分開的話,也有利于邏輯服務(wù)器的設(shè)計(jì)。







            登陸(連接)服務(wù)器的設(shè)計(jì):



               在網(wǎng)絡(luò)游戲里,其中一個(gè)很大的難點(diǎn)就是玩家與服務(wù)器的通訊,在Windos的服務(wù)器架構(gòu)下,網(wǎng)絡(luò)游戲服務(wù)器端采用的I/O模型,通常是完成端口。在項(xiàng)目 開始時(shí)研究完成端口,感覺很難,根本看不懂,因?yàn)樗诤芏嗟胤脚c以前寫網(wǎng)絡(luò)通訊軟件時(shí)用的方法不同。但是當(dāng)我分析過3個(gè)完成端口的程序后,基本了解的它的 使用方法。而且在懂以后,回過頭來看,其它完成端口的概念也不是很復(fù)雜,只要能清楚的了解幾個(gè)函數(shù)的使用方法以及基本的處理框架流程,你就會(huì)發(fā)現(xiàn)它其實(shí)非 常的簡單。



               完成端口的一些需要理解的地方:



            1。消息隊(duì)列



            2。工作線程



            3。網(wǎng)絡(luò)消息返回結(jié)構(gòu)體







               一般我們在設(shè)計(jì)服務(wù)器端的時(shí)候,最關(guān)鍵的地方是如何分辯剛剛收到的網(wǎng)絡(luò)數(shù)據(jù)是由那個(gè)玩家發(fā)送過來的,如果是采用消息事件驅(qū)動(dòng)的話,是可以得到一個(gè) socket的值,然后再用這個(gè)值與系統(tǒng)里存在的socket進(jìn)行比對,這樣就可以得到是那位玩家發(fā)送過來的游戲消息。我在還沒有使用完成端口的時(shí)候,就 是使用這個(gè)方法。這樣的設(shè)計(jì)有一個(gè)缺點(diǎn)就是每次收到數(shù)據(jù)的時(shí)候回浪費(fèi)很多時(shí)間在于確定消息發(fā)送者身份上。但是在完成端口的設(shè)計(jì)里,我們可以采用一個(gè)取巧的 方法進(jìn)行設(shè)計(jì)。所以,這個(gè)問題很輕易的就結(jié)局了,而且系統(tǒng)開銷也不是很大,關(guān)于完成端口,可以參考一下的文章:



            《關(guān)于Winsock異步I/O模型中的事件模型》



            http://search.csdn.net/Expert/topic/166/166227.xml?temp=.4639093



            《手把手教你玩轉(zhuǎn)SOCKET模型之重疊I/O篇》



            http://blog.csdn.net/piggyxp/archive/2004/09/23/114883.aspx



            《學(xué)習(xí)日記]IOCP的學(xué)習(xí)--初步理解》



            http://www.gameres.com/bbs/showthread.asp?threadid=25898



            《用完成端口開發(fā)大響應(yīng)規(guī)模的Winsock應(yīng)用程序》



            http://www.xiaozhou.net/ReadNews.asp?NewsID=901



            《理解I/O Completion Port》



            http://dev.gameres.com/Program/Control/IOCP.htm



            幾個(gè)關(guān)鍵函數(shù)的說明:



            http://msdn.microsoft.com/library/en-us/fileio/fs/postqueuedcompletionstatus.asp?frame=true



            http://msdn.microsoft.com/library/en-us/fileio/fs/createiocompletionport.asp?frame=true



            http://msdn.microsoft.com/library/en-us/fileio/fs/getqueuedcompletionstatus.asp?frame=true



            http://msdn.microsoft.com/library/en-us/winsock/winsock/wsarecv_2.asp?frame=true







            如果你能認(rèn)真的搞清楚上面的東西,我估計(jì)你離理解完成端口就只有一步了。剩下的這一步就是自己編碼實(shí)現(xiàn)一個(gè)下了。有些時(shí)候,看得懂了不一定會(huì)實(shí)際應(yīng)用,不實(shí)實(shí)在在的寫一點(diǎn)程序,驗(yàn)證一下你的想法,是不會(huì)真正搞清楚原理的。







            不 過除非你想深入的研究網(wǎng)絡(luò)技術(shù),否則只要知道怎么用就可以了,剩下的就是尋找一個(gè)合適的別人封裝好的類來使用。這樣可以節(jié)省你很多的事件,當(dāng)然拿來的東西 最好有源代碼,這樣如果發(fā)生什么問題,你也好確定是在那個(gè)地方出錯(cuò),要改或者擴(kuò)充功能都會(huì)方便很多。當(dāng)然,還要注意人家的版權(quán),最好在引用別人代碼的地方 加一些小小的注解,這樣用不了多少時(shí)間,而且對你,對原作者都有好處^_^。







            不過在 完成端口上我還是沒有成為拿來主義者,還是自己封裝了完成端口的操作,原因找到的源代碼代碼封裝的接口函數(shù)我怎么看怎么覺得別扭,所以最后還是自己封裝了 一個(gè)完成端口,有興趣的可以去看我的源代碼,里面有很詳細(xì)的注解。而且就我看來,要拿我封裝的完成端口類使用起來還是很簡單的。使用的時(shí)候,只要繼承我的 CIOCP,然后,根據(jù)需要覆蓋3個(gè)虛函數(shù)(OnAccept,OnRead,OnClose)就可以了,最多是在連接函數(shù)里,需要用一個(gè)函數(shù)去設(shè)置一下 完成端口信息。當(dāng)然,我封裝的類稍微簡單了一些,如果要拿來響應(yīng)大規(guī)模連接,還是存在很多的問題,但是如果只是針對少量連接,還是可以應(yīng)付的。







            對 于客戶端的I/O模型,我就沒有那么用心的去尋找什么好的解決方案,采用了一個(gè)最簡單的,最原始的阻塞線程的方法做。原理很簡單:創(chuàng)建一個(gè)sockt,把 socket設(shè)置為阻塞,連接服務(wù)器成功后,啟動(dòng)一個(gè)線程,在線程里面用recv()等待服務(wù)器發(fā)過來的消息。在我的代碼里,也是把阻塞線程的方法封裝成 一個(gè)類,在使用的時(shí)候,先繼承TClientSocket,然后覆蓋(重載)里面的OnRead()函數(shù),并在里面寫入一些處理收到數(shù)據(jù)后的操作代碼。在 用的時(shí)候,只要connect成功,系統(tǒng)就會(huì)自動(dòng)啟動(dòng)一個(gè)接收線程,一旦有數(shù)據(jù)就觸發(fā)剛才覆蓋的OnRead函數(shù)。這個(gè)類我也不是完全直接寫的,在里面使 用了別人的一些代碼,主要是讓每個(gè)類都能把線程封裝起來,這樣在創(chuàng)建不同的類的實(shí)體的時(shí)候,每個(gè)類的實(shí)體自己都會(huì)有一個(gè)單獨(dú)的數(shù)據(jù)接收線程。



            當(dāng) 然除了阻塞線程的方法,比較常用的還有就是用消息事件的方法收取數(shù)據(jù)了。我剛開始的時(shí)候,也是采用這個(gè)方法(以前用過^_^),但是后來發(fā)現(xiàn)不太好封裝, 最后采用阻塞線程的方法,這樣做還有一個(gè)好處可以讓我的代碼看起來更加舒服一些。不過就我分析《航海世紀(jì)》客戶端采用的是消息事件的I/O模型。其它的網(wǎng) 絡(luò)游戲就不太清楚了,我想也應(yīng)該是采用消息事件方式的吧。。



               我記得在gameres上看到過某人寫的一篇關(guān)于完成端口的筆記,他在篇末結(jié)束的時(shí)候,提出一個(gè)思考題:我們在學(xué)習(xí)完成端口的時(shí)候,都知道它是用于server端的操作,而且很多文章也是這樣寫的,但是不知道有沒有考慮過,用完成端口做客戶端來使用?



               其實(shí)這個(gè)問題很好回答,答案是OK。拿IOCP做客戶端也是可行的,就以封裝的IOCP為例,只要在繼承原來的CIOCP類的基礎(chǔ)上,再寫一個(gè)Connect(char * ip, int port)的函數(shù),就可以實(shí)現(xiàn)客戶端的要求了。
            1. bool CIOCPClient::Connect(char *ip, int port)  
            2. {  
            3.         //  連接服務(wù)器  
            4.   
            5.     if (!bInit)  
            6.   
            7.         if (!Init())  
            8.   
            9.             return false;  
            10.   
            11.     //  初始化連接socket  
            12.     SOCKET m_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);  
            13.   
            14.     if (m_socket == SOCKET_ERROR)  
            15.         return false;  
            16.   
            17.     // 填寫服務(wù)器地址信息  
            18.   
            19.     sockaddr_in ClientAddr;  
            20.   
            21.     ClientAddr.sin_family = AF_INET;  
            22.   
            23.     ClientAddr.sin_port = htons(port);      
            24.   
            25.     ClientAddr.sin_addr.s_addr = inet_addr(ip);  
            26.     // 綁定監(jiān)聽端口  
            27.     bind(m_socket, (SOCKADDR *)&ClientAddr, sizeof(ClientAddr));  
            28.   
            29.     if (connect(m_socket, (SOCKADDR *)&ClientAddr, sizeof(ClientAddr)) == SOCKET_ERROR)  
            30.   
            31.         return false;  
            32.     this->m_workThread = true;  
            33.   
            34.   
            35.   
            36.     g_hwThread = CreateThread(NULL, 0, WorkThread, (LPVOID)this, 0, &m_wthreadID);  //  創(chuàng)建工作線程,用來處理完成端口消息的  
            37.     this->SetIoCompletionPort(m_socket, NULL);  //  設(shè)置完成端口監(jiān)聽的socket  
            38.     return true;  
            39.   
            40. }  

            前面一段是用來連接服務(wù)器,所有的客戶端程序都是要這樣做的,當(dāng)連接成功后,m_socket就是我們想要的用于與服務(wù)器 端通訊的socket,然后,我們啟動(dòng)工作線程,并使用SetIoCompletionPort來設(shè)置完成端口監(jiān)聽的socket。只要在原來的基礎(chǔ)上增 加一個(gè)函數(shù),就可以把用于服務(wù)器的ICOP變成用于客戶端的IOCP。



               在收到網(wǎng)絡(luò)數(shù)據(jù)以后,下一步就是根據(jù)需要,把收到的網(wǎng)絡(luò)數(shù)據(jù)包轉(zhuǎn)變?yōu)橛螒蛳?shù)據(jù)包。在轉(zhuǎn)換之前,首先是要從收到的網(wǎng)絡(luò)數(shù)據(jù)里面提取出有效的消息。這里 為什么說是要提取有效部分?其主要原因是,我們創(chuàng)建的游戲消息數(shù)據(jù),在進(jìn)行網(wǎng)絡(luò)傳輸?shù)臅r(shí)候,不是以消息的長度來傳的,而是根據(jù)系統(tǒng)在接收到發(fā)送數(shù)據(jù)請求的 時(shí)候,根據(jù)實(shí)際情況來發(fā)送的。例如我這里有一條很長的游戲消息,有3k,但是系統(tǒng)一次只能發(fā)送1k的數(shù)據(jù),所以,我們的游戲消息,只能把我們的游戲消息分 為3個(gè)包,分3次發(fā)送,這樣在我們接收消息的時(shí)候,就會(huì)觸發(fā)3次OnRead,而這3次OnRead收到的數(shù)據(jù)都不是一次完整的游戲消息。所以,我們在收 到網(wǎng)絡(luò)數(shù)據(jù)后,要先和上一次收到的網(wǎng)絡(luò)數(shù)據(jù)進(jìn)行合并,然后再在里面提取出有效的游戲消息,并在提取后,把已經(jīng)提取的部分刪除。我在這里把這一步操作封裝到 一個(gè)類里CBuftoMsg。這里順便說明一下:一條游戲消息的網(wǎng)絡(luò)數(shù)據(jù)包是以0x00EEEE(16進(jìn)制)為結(jié)束標(biāo)記(《航海世紀(jì)》的做法)。
            1. struct TMessage  
            2.   
            3. {  
            4.   
            5.     char * p;       //  消息頭所在的位置  
            6.   
            7.   
            8.   
            9.     long len;       //  整個(gè)消息的長度  
            10.   
            11.   
            12.   
            13. };  
            14.   
            15.   
            16.   
            17.   
            18.    
            19.   
            20.   
            21. class CBuftoMsg  
            22.   
            23.   
            24.   
            25. {  
            26.   
            27.   
            28.   
            29. protected:  
            30.   
            31.   
            32.   
            33.     char msgbuf[BUF_LEN];     
            34.   
            35.   
            36.   
            37.     char * buf_end;  
            38.   
            39.   
            40.   
            41.     char * buf_begin;  
            42.   
            43.   
            44.   
            45.     int buf_len;  
            46.   
            47.   
            48.   
            49. public:  
            50.   
            51.   
            52.   
            53.     CBuftoMsg(void);  
            54.   
            55.   
            56.   
            57.     TMessage getMessage(void);  
            58.   
            59.   
            60.   
            61.     void cleanup_buf(void);  
            62.   
            63.   
            64.   
            65.     bool AddMsgBuf(const char *, int);  
            66.   
            67.   
            68.   
            69.     int tag;  
            70.   
            71.   
            72.   
            73. };  
            74.   
            75.   
            76.   
            77.   
            78.    
            79.   
            80.   
            81. CBuftoMsg::CBuftoMsg(void)  
            82.   
            83.   
            84.   
            85. {  
            86.   
            87.   
            88.   
            89.     buf_begin = msgbuf;  
            90.   
            91.   
            92.   
            93.     buf_end = msgbuf;  
            94.   
            95.   
            96.   
            97.     buf_len = 0;  
            98.   
            99.   
            100.   
            101. }  
            102.   
            103.   
            104.   
            105.   
            106.    
            107.   
            108.   
            109. TMessage CBuftoMsg::getMessage()  
            110.   
            111.   
            112.   
            113. {  
            114.   
            115.   
            116.   
            117.     char * p    = buf_begin;  
            118.   
            119.   
            120.   
            121.     TMessage result;  
            122.   
            123.   
            124.   
            125.     result.len  = 0;  
            126.   
            127.   
            128.   
            129.     result.p    = NULL;  
            130.   
            131.   
            132.   
            133.     while(p <= buf_begin + buf_len - 2)  
            134.   
            135.   
            136.   
            137.     {  
            138.   
            139.   
            140.   
            141.         if ( *p == 0x00)  
            142.   
            143.   
            144.   
            145.         {  
            146.   
            147.   
            148.   
            149.             const static char ce = 0xEE;  
            150.   
            151.   
            152.   
            153.             if (*(p + 1) == ce)  
            154.   
            155.   
            156.   
            157.                 if(*(p + 2) == ce)  
            158.   
            159.   
            160.   
            161.                 {  
            162.   
            163.   
            164.   
            165.                     //  每條消息都是以 00 EE EE 為結(jié)束標(biāo)志  
            166.   
            167.   
            168.   
            169.                     result.p    = buf_begin;  
            170.   
            171.   
            172.   
            173.                     result.len  = p - buf_begin + 3;  
            174.   
            175.   
            176.   
            177.                     buf_begin   =  p + 3;  
            178.   
            179.   
            180.   
            181.                     buf_end     = buf_begin + buf_len;  
            182.   
            183.   
            184.   
            185.                     buf_len -= result.len;  
            186.   
            187.   
            188.   
            189.                     break;  
            190.   
            191.   
            192.   
            193.                 }  
            194.   
            195.   
            196.   
            197.         }  
            198.   
            199.   
            200.   
            201.         p++;  
            202.   
            203.   
            204.   
            205.     }  
            206.   
            207.   
            208.   
            209.     return result;  
            210.   
            211.   
            212.   
            213. }  
            214.   
            215.   
            216.   
            217.   
            218.    
            219.   
            220.   
            221. void CBuftoMsg::cleanup_buf()  
            222.   
            223.   
            224.   
            225. {  
            226.   
            227.   
            228.   
            229.     if (buf_len < BUF_LEN)  
            230.   
            231.   
            232.   
            233.     {  
            234.   
            235.   
            236.   
            237.         if (buf_len == 0)  
            238.   
            239.   
            240.   
            241.         {  
            242.   
            243.   
            244.   
            245.             buf_begin   = msgbuf;  
            246.   
            247.   
            248.   
            249.             buf_end     = msgbuf;  
            250.   
            251.   
            252.   
            253.         }  
            254.   
            255.   
            256.   
            257.         else  
            258.   
            259.   
            260.   
            261.         {  
            262.   
            263.   
            264.   
            265.             memmove(msgbuf, buf_end - buf_len, buf_len);  
            266.   
            267.   
            268.   
            269.             buf_begin = msgbuf;  
            270.   
            271.   
            272.   
            273.             buf_end = buf_end - buf_len;  
            274.   
            275.         }  
            276.   
            277.   
            278.     }  
            279.   
            280.     else  
            281.   
            282.     {  
            283.   
            284.         //  加入緩沖區(qū)的數(shù)據(jù)過多,要拋棄原來的內(nèi)容  
            285.   
            286.         buf_begin   = msgbuf;  
            287.   
            288.         buf_end     = msgbuf;  
            289.   
            290.         buf_len     = 0;  
            291.   
            292.     }  
            293.   
            294. }  
            295.   
            296. bool CBuftoMsg::AddMsgBuf(const char * buf, int len)  
            297. {  
            298.   
            299.     if (len < 1)  
            300.   
            301.         return false;  
            302.   
            303.     bool result = true;  
            304.   
            305.     buf_len += len;  
            306.   
            307.     if (buf_len >= BUF_LEN)     //  如果緩沖區(qū)裝滿了則直接把原來的緩沖區(qū)清空再重新復(fù)制數(shù)據(jù)  
            308.     {  
            309.         this->cleanup_buf();      
            310.         result = false;  
            311.   
            312.     }  
            313.   
            314.     memcpy(buf_begin, buf, len);  
            315.   
            316.     return result;  
            317.   
            318. }  


            我在這里把 CBuftoMsg 的代碼貼出來,主要是因?yàn)椋以趯懕疚牡臅r(shí)候,發(fā)現(xiàn)一個(gè)驚天動(dòng)地的bug,有興趣的讀者可以自己去找一下。不過一開始寫代碼的時(shí)候,還不是這樣的,當(dāng)初的 代碼bug比這個(gè)還要多,問題還要嚴(yán)重,嚴(yán)重到經(jīng)常讓服務(wù)器程序莫名其妙的崩潰,而且這個(gè)問題,一直到5月份,系統(tǒng)在進(jìn)行集成測試的時(shí)候才發(fā)現(xiàn)并解決(還 沒有徹底解決,至少目前我還發(fā)現(xiàn)了bug,),以前一直都沒有怎么注意到這個(gè)問題,而且我們還把因?yàn)檫@個(gè)bug造成的問題,歸結(jié)到線程的互斥上去^_^!







            我的登陸服務(wù)器,除了基本的處理網(wǎng)絡(luò)數(shù)據(jù)包以外,還負(fù)責(zé)玩家系統(tǒng)的登陸驗(yàn)證,這部分東西不是很復(fù)雜,在我的程序里,只是簡單的從ini文件里讀取玩家的信息而已,有興趣的自己去看我的代碼(不過這部分遠(yuǎn)還沒有真正的完善,存在很多問題)。







            除 了登陸驗(yàn)證以外,在登陸程序還負(fù)責(zé)進(jìn)行消息轉(zhuǎn)發(fā),就是把客戶端的消息分別發(fā)送到不同的服務(wù)器。如果當(dāng)初設(shè)計(jì)的是一個(gè)邏輯服務(wù)器,這個(gè)功能就可以簡單很多, 只要發(fā)送到一個(gè)服務(wù)器里就可以了。現(xiàn)在的要發(fā)到2個(gè)服務(wù)器,所以還需要對收到的游戲消息進(jìn)行分類。為了方便,我對原來定義消息的ID進(jìn)行了分類,所以,在 GameMessageID.h文件里定義的游戲消息對應(yīng)的ID編號不是順序編排的。不過也因?yàn)檫@樣,在現(xiàn)在看來,這樣的設(shè)計(jì),有一些不太好。在整個(gè)系統(tǒng) 里,存在有4個(gè)主體,他們之間互相發(fā)送,就用了12組的數(shù)據(jù),為了方便計(jì)算,我把一個(gè)變量的范圍分為16個(gè)不同的區(qū)域,這樣每個(gè)區(qū)域只有16個(gè)值可以用 (我這里是用char類型256/16=16)。在加上用另外一個(gè)變量表示邏輯上上的分類(目前按照功能分了12組,有登陸、貿(mào)易、銀行、船廠等)這樣對 于貿(mào)易這個(gè)類型的游戲消息,從客戶端發(fā)送到邏輯服務(wù)器上,只能有16中可能性,如果要發(fā)送更多消息,可能要增加另外一個(gè)邏輯分類:貿(mào)易2^_^!當(dāng)初這樣 的設(shè)計(jì)只是想簡化一下系統(tǒng)的處理過程,不過卻造成了系統(tǒng)的擴(kuò)充困難,要解決也不是沒有辦法,把類型分類的變量由char類型,改為int類型,這樣對一個(gè) 變量分區(qū),在范圍上會(huì)款很多,而且不會(huì)造成邏輯分類上的困擾,但是,這樣存在一個(gè)弊端就是就是每條網(wǎng)絡(luò)消息數(shù)據(jù)包的長度增加了一點(diǎn)點(diǎn)。不要小看這一個(gè)字節(jié) 的變量,現(xiàn)在設(shè)計(jì)的一條游戲消息頭的長度是10個(gè)字節(jié),如果把char改為int,無形中就增加了3個(gè)字節(jié),在和原來的比較,這樣每條消息在消息頭部分, 就多出23%,也就是我們100M的網(wǎng)絡(luò)現(xiàn)在只能利用77%而已。



               ^_^呵呵看出什么問題沒有?



               沒有,那我告訴你,有一個(gè)概念被偷換了,消息頭的數(shù)據(jù)不等于整條游戲的消息數(shù)據(jù),所以,消息頭部分雖然多出了23%,但是整條游戲消息并不會(huì)增加這么 多,最多增加17%,最少應(yīng)該不會(huì)操作5%。平均起來,應(yīng)該在10%左右(游戲消息里,很多消息的實(shí)際部分可能就一個(gè)int變量而已)。不過,就算是 10%,也占用了帶寬。



               ^_^呵呵還看出什么問題沒有?



               ^_^先去讀一下我的代碼,再回頭看看,上面的論述還有什么問題。



               實(shí)際上,每條游戲消息由:消息頭、消息實(shí)體、結(jié)束標(biāo)記組成,其中固定的是消息頭和結(jié)束標(biāo)記,所以,實(shí)際上一條實(shí)際上游戲消息的數(shù)據(jù)包,最多比原來的多15%,平均起來,應(yīng)該是8%~10%的增量而異。



               好了,不在這個(gè)計(jì)算細(xì)節(jié)上扣太多精力了。要解決這個(gè)問題,要么是增加網(wǎng)絡(luò)數(shù)據(jù)的發(fā)送量,要么,就是調(diào)整游戲結(jié)構(gòu),例如,把兩個(gè)功能服務(wù)器合并為一個(gè)服務(wù) 器,這樣服務(wù)器的對象實(shí)體就由原來的4個(gè)分為3個(gè),兩兩間的通訊,就由原來的12路縮減為6路,只要分8個(gè)區(qū)域就ok了。這樣每個(gè)邏輯分類就有32條游戲 消息可以使用。當(dāng)然,如果進(jìn)一步合并服務(wù)器,把服務(wù)器端都合并到一個(gè)程序,那就不用分類了^_^!

               在登陸服務(wù)器目錄下,還有一組mynet.h/mynet.cpp的文件,是我當(dāng)初為服務(wù)器端設(shè)計(jì)的函數(shù),封裝的是消息事件網(wǎng)絡(luò)響應(yīng)模型。只不過封裝得 不是怎么好,被拋棄不用了,有興趣的可以去看看,反正我是不推薦看的。只不過是在這里說明一下整個(gè)工程目錄的結(jié)構(gòu)而已。
            posted on 2009-09-14 13:26 暗夜教父 閱讀(371) 評論(0)  編輯 收藏 引用 所屬分類: Game Development

            <2009年9月>
            303112345
            6789101112
            13141516171819
            20212223242526
            27282930123
            45678910

            常用鏈接

            留言簿(2)

            隨筆分類

            隨筆檔案

            文章分類

            文章檔案

            搜索

            •  

            最新評論

            閱讀排行榜

            評論排行榜

            一本色道久久88综合日韩精品 | 欧美无乱码久久久免费午夜一区二区三区中文字幕 | 久久不射电影网| 久久亚洲中文字幕精品有坂深雪| 综合久久一区二区三区| 久久99国产一区二区三区| 亚洲国产成人久久综合碰碰动漫3d | 青青青青久久精品国产| 国产精品久久久久jk制服| 久久偷看各类wc女厕嘘嘘| 亚洲成色www久久网站夜月| 久久中文字幕人妻丝袜| 久久亚洲日韩看片无码| 久久这里只有精品首页| 亚洲午夜精品久久久久久浪潮| 老司机午夜网站国内精品久久久久久久久| 超级碰久久免费公开视频| 久久er国产精品免费观看2| 嫩草影院久久国产精品| 亚洲狠狠综合久久| 久久综合视频网站| 成人久久免费网站| 少妇精品久久久一区二区三区| 久久婷婷五月综合97色一本一本| 久久亚洲国产成人精品性色| 久久精品九九亚洲精品| 久久久综合九色合综国产| 久久国产精品免费一区二区三区| 久久国产香蕉视频| 久久精品国产免费观看三人同眠| 久久一日本道色综合久久| 青青青伊人色综合久久| 久久青青国产| 久久久噜噜噜www成人网| 国产精品日韩欧美久久综合| 狠狠色丁香婷婷久久综合五月 | 久久免费小视频| 亚洲国产成人精品无码久久久久久综合 | 亚洲精品97久久中文字幕无码| 亚洲国产精品18久久久久久| 99久久精品国产高清一区二区 |