<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    posts - 56,  comments - 12,  trackbacks - 0

    本文作者:sodme
    本文出處:http://blog.csdn.net/sodme
    聲明:本文可以不經作者同意任意轉載、復制、傳播,但任何對本文的引用都請保留作者、出處及本聲明信息。謝謝!

    常見的網絡服務器,基本上是7*24小時運轉的,對于網游來說,至少要求服務器要能連續(xù)工作一周以上的時間并保證不出現服務器崩潰這樣的災難性事件。事 實上,要求一個服務器在連續(xù)的滿負荷運轉下不出任何異常,要求它設計的近乎完美,這幾乎是不太現實的。服務器本身可以出異常(但要盡可能少得出),但是, 服務器本身應該被設計得足以健壯,“小病小災”打不垮它,這就要求服務器在異常處理方面要下很多功夫。

      服務器的異常處理包括的內容非常廣泛,本文僅就在網絡封包方面出現的異常作一討論,希望能對正從事相關工作的朋友有所幫助。

    關于網絡封包方面的異常,總體來說,可以分為兩大類:一是封包格式出現異常;二是封包內容(即封包數據)出現異常。在封包格式的異常處理方面,我們在最 底端的網絡數據包接收模塊便可以加以處理。而對于封包數據內容出現的異常,只有依靠游戲本身的邏輯去加以判定和檢驗。游戲邏輯方面的異常處理,是隨每個游 戲的不同而不同的,所以,本文隨后的內容將重點闡述在網絡數據包接收模塊中的異常處理。

      為方便以下的討論,先明確兩個概念(這兩個概念是為了敘述方面,筆者自行取的,并無標準可言):
      1、邏輯包:指的是在應用層提交的數據包,一個完整的邏輯包可以表示一個確切的邏輯意義。比如登錄包,它里面就可以含有用戶名字段和密碼字段。盡管它看上去也是一段緩沖區(qū)數據,但這個緩沖區(qū)里的各個區(qū)間是代表一定的邏輯意義的。
      2、物理包:指的是使用recv(recvfrom)或wsarecv(wsarecvfrom)從網絡底層接收到的數據包,這樣收到的一個數據包,能不能表示一個完整的邏輯意義,要取決于它是通過UDP類的“數據報協(xié)議”發(fā)的包還是通過TCP類的“流協(xié)議”發(fā)的包。

    我們知道,TCP是流協(xié)議,“流協(xié)議”與“數據報協(xié)議”的不同點在于:“數據報協(xié)議”中的一個網絡包本身就是一個完整的邏輯包,也就是說,在應用層使用 sendto發(fā)送了一個邏輯包之后,在接收端通過recvfrom接收到的就是剛才使用sendto發(fā)送的那個邏輯包,這個包不會被分開發(fā)送,也不會與其 它的包放在一起發(fā)送。但對于TCP而言,TCP會根據網絡狀況和neagle算法,或者將一個邏輯包單獨發(fā)送,或者將一個邏輯包分成若干次發(fā)送,或者會將 若干個邏輯包合在一起發(fā)送出去。正因為TCP在邏輯包處理方面的這種粘合性,要求我們在作基于TCP的應用時,一般都要編寫相應的拼包、解包代碼。

      因此,基于TCP的上層應用,一般都要定義自己的包格式。TCP的封包定義中,除了具體的數據內容所代表的邏輯意義之外,第一步就是要確定以何種方式表示當前包的開始和結束。通常情況下,表示一個TCP邏輯包的開始和結束有兩種方式:
      1、以特殊的開始和結束標志表示,比如FF00表示開始,00FF表示結束。
      2、直接以包長度來表示。比如可以用第一個字節(jié)表示包總長度,如果覺得這樣的話包比較小,也可以用兩個字節(jié)表示包長度。

      下面將要給出的代碼是以第2種方式定義的數據包,包長度以每個封包的前兩個字節(jié)表示。我將結合著代碼給出相關的解釋和說明。

      函數中用到的變量說明:

      CLIENT_BUFFER_SIZE:緩沖區(qū)的長度,定義為:Const int CLIENT_BUFFER_SIZE=4096。
      m_ClientDataBuf:數據整理緩沖區(qū),每次收到的數據,都會先被復制到這個緩沖區(qū)的末尾,然后由下面的整理函數對這個緩沖區(qū)進行整理。它的定義是:char m_ClientDataBuf[2* CLIENT_BUFFER_SIZE]。
      m_DataBufByteCount:數據整理緩沖區(qū)中當前剩余的未整理字節(jié)數。
      GetPacketLen(const char*):函數,可以根據傳入的緩沖區(qū)首址按照應用層協(xié)議取出當前邏輯包的長度。
      GetGamePacket(const char*, int):函數,可以根據傳入的緩沖區(qū)生成相應的游戲邏輯數據包。
      AddToExeList(PBaseGamePacket):函數,將指定的游戲邏輯數據包加入待處理的游戲邏輯數據包隊列中,等待邏輯處理線程對其進行處理。
      DATA_POS:指的是除了包長度、包類型等這些標志型字段之外,真正的數據包內容的起始位置。

    Bool SplitFun(const char* pData,const int &len)
    {
        PBaseGamePacket pGamePacket=NULL;
        __int64 startPos=0, prePos=0, i=0;
        int packetLen=0;

      //先將本次收到的數據復制到整理緩沖區(qū)尾部
        startPos = m_DataBufByteCount;  
        memcpy( m_ClientDataBuf+startPos, pData, len );
        m_DataBufByteCount += len;   

        //當整理緩沖區(qū)內的字節(jié)數少于DATA_POS字節(jié)時,取不到長度信息則退出
     //注意:退出時并不置m_DataBufByteCount為0
        if (m_DataBufByteCount < DATA_POS+1)
            return false; 

        //根據正常邏輯,下面的情況不可能出現,為穩(wěn)妥起見,還是加上
        if (m_DataBufByteCount >  2*CLIENT_BUFFER_SIZE)
        {
            //設置m_DataBufByteCount為0,意味著丟棄緩沖區(qū)中的現有數據
            m_DataBufByteCount = 0;

      //可以考慮開放錯誤格式數據包的處理接口,處理邏輯交給上層
      //OnPacketError()
            return false;
        }

         //還原起始指針
         startPos = 0;

         //只有當m_ClientDataBuf中的字節(jié)個數大于最小包長度時才能執(zhí)行此語句
        packetLen = GetPacketLen( pIOCPClient->m_ClientDataBuf );

        //當邏輯層的包長度不合法時,則直接丟棄該包
        if ((packetLen < DATA_POS+1) || (packetLen > 2*CLIENT_BUFFER_SIZE))
        {
            m_DataBufByteCount = 0;

      //OnPacketError()
            return false;
        }

        //保留整理緩沖區(qū)的末尾指針
        __int64 oldlen = m_DataBufByteCount; 

        while ((packetLen <= m_DataBufByteCount) && (m_DataBufByteCount>0))
        {
            //調用拼包邏輯,獲取該緩沖區(qū)數據對應的數據包
            pGamePacket = GetGamePacket(m_ClientDataBuf+startPos, packetLen); 

            if (pGamePacket!=NULL)
            {
                //將數據包加入執(zhí)行隊列
                AddToExeList(pGamePacket);
            }

            pGamePacket = NULL;
     
      //整理緩沖區(qū)的剩余字節(jié)數和新邏輯包的起始位置進行調整
            m_DataBufByteCount -= packetLen;
            startPos += packetLen; 

            //殘留緩沖區(qū)的字節(jié)數少于一個正常包大小時,只向前復制該包隨后退出
            if (m_DataBufByteCount < DATA_POS+1)
            {
                for(i=startPos; i<startPos+m_DataBufByteCount; ++i)
                    m_ClientDataBuf[i-startPos] = m_ClientDataBuf[i];

                return true;
            }

            packetLen = GetPacketLen(m_ClientDataBuf + startPos );

             //當邏輯層的包長度不合法時,丟棄該包及緩沖區(qū)以后的包
            if ((packetLen<DATA_POS+1) || (packetLen>2*CLIENT_BUFFER_SIZE))
            {
                m_DataBufByteCount = 0;

          //OnPacketError()
                return false;
            }

             if (startPos+packetLen>=oldlen)
            {
                for(i=startPos; i<startPos+m_DataBufByteCount; ++i)
                    m_ClientDataBuf[i-startPos] = m_ClientDataBuf[i];          

                return true;
            }
         }//取所有完整的包

         return true;
    }

      以上便是數據接收模塊的處理函數,下面是幾點簡要說明:

      1、用于拼包整理的緩沖區(qū)(m_ClientDataBuf)應該比recv中指定的接收緩沖區(qū)(pData)長度(CLIENT_BUFFER_SIZE)要大,通常前者是后者的2倍(2*CLIENT_BUFFER_SIZE)或更大。

      2、為避免因為剩余數據前移而導致的額外開銷,建議m_ClientDataBuf使用環(huán)形緩沖區(qū)實現。

    3、為了避免出現無法拼裝的包,我們約定每次發(fā)送的邏輯包,其單個邏輯包最大長度不可以超過CLIENT_BUFFER_SIZE的2倍。因為我們的整 理緩沖區(qū)只有2*CLIENT_BUFFER_SIZE這么長,更長的數據,我們將無法整理。這就要求在協(xié)議的設計上以及最終的發(fā)送函數的處理上要加上這 樣的異常處理機制。


      4、對于數據包過短或過長的包,我們通常的情況是置m_DataBufByteCount為0,即舍棄當前 包的處理。如果此處不設置m_DataBufByteCount為0也可,但該客戶端只要發(fā)了一次格式錯誤的包,則其后繼發(fā)過來的包則也將連帶著產生格式 錯誤,如果設置m_DataBufByteCount為0,則可以比較好的避免后繼的包受此包的格式錯誤影響。更好的作法是,在此處開放一個封包格式異常 的處理接口(OnPacketError),由上層邏輯決定對這種異常如何處置。比如上層邏輯可以對封包格式方面出現的異常進行計數,如果錯誤的次數超過 一定的值,則可以斷開該客戶端的連接。

    5、建議不要在recv或wsarecv的函數后,就緊接著作以上的處理。當recv收到一段數 據后,生成一個結構體或對象(它主要含有data和len兩個內容,前者是數據緩沖區(qū),后者是數據長度),將這樣的一個結構體或對象放到一個隊列中由后面 的線程對其使用SplitFun函數進行整理。這樣,可以最大限度地提高網絡數據的接收速度,不至因為數據整理的原因而在此處浪費時間。
    posted on 2007-04-07 22:07 苦笑枯 閱讀(328) 評論(0)  編輯  收藏 所屬分類: Other
    收藏來自互聯(lián)網,僅供學習。若有侵權,請與我聯(lián)系!

    <2007年4月>
    25262728293031
    1234567
    891011121314
    15161718192021
    22232425262728
    293012345

    常用鏈接

    留言簿(2)

    隨筆分類(56)

    隨筆檔案(56)

    搜索

    •  

    最新評論

    閱讀排行榜

    評論排行榜

    主站蜘蛛池模板: 妞干网在线免费视频| 亚洲女同成av人片在线观看 | 亚洲国产精品一区二区第一页| 免费人成网站在线观看不卡| 亚洲男人天堂2018av| 四虎免费久久影院| 一级成人a毛片免费播放| 亚洲大码熟女在线观看| 亚洲乱码中文字幕久久孕妇黑人| 精品免费久久久久久久| 成人免费网站久久久| 亚洲理论在线观看| 亚洲成a人片在线观看久| 亚洲网站免费观看| 免费看黄网站在线看| 亚洲国产成人九九综合| 亚洲熟妇无码AV在线播放| 大地资源在线观看免费高清| 中文成人久久久久影院免费观看| 亚洲人成电影网站色www| 亚洲Av熟妇高潮30p| 亚洲精品成人区在线观看| 免费人成视频在线| 无码精品国产一区二区三区免费 | 国产免费人成视频在线播放播| 亚洲人成影院午夜网站| 亚洲人成在线播放网站| 免费日本黄色网址| 午夜国产精品免费观看| 中文字幕无码一区二区免费| 国产亚洲精品仙踪林在线播放| 亚洲噜噜噜噜噜影院在线播放| 亚洲色中文字幕无码AV| 免费一级做a爰片久久毛片潮喷| 少妇高潮太爽了在线观看免费| 国产在线精品免费aaa片| 一个人看的www视频免费在线观看| 亚洲一区二区观看播放| 亚洲国产成人精品青青草原| 亚洲成a人片在线观看无码| 国产亚洲精品拍拍拍拍拍|