熱血傳奇Rungate源代碼分析筆記。

   RunGate有三個Socket對象  一個向M2發送  一個接收客戶端的連接。還有一個連接控制檯(當然這個是和控制檯綁定的通常手動啓動是不需要的 只是在私服這方面方便私服架設      者使用)

   控制檯Socket會每隔一段時間向控制檯發送保活包。如果沒有發送 那麼控制檯肯定是知道rungate沒反應了或者被關閉了 然後可以立馬又開啓。純粹是爲了私服假者簡單而考      慮。

  實際只有2個Socket對象纔是最主要的。M2Sokcet和ClientSocket。

  整個結構當然是使用的典型生產者消費者模型。 但是代碼結構很不清晰.而且消息的處理是使用Timer來處理,而不是線程。

 程序在通過一個常量定義了在棧上的會話數量。當程序開始運行會 在棧上有一個數組 保存會話信息。  會話信息是一個結構體,內部記錄了會話的信息,比如連接時間,封包大小

 以及 上次移動時間 上次通訊時間等等。

 當有客戶端連接時候。會在預分頻的數組內找到一個未使用的空間。初始化這個數組的內容 將當前的客戶端連接保持在這個會話之內。

 當收到客戶端的信息時候,會進行一系列合法的驗證 或者保護。最終將消息生成一個消息體:

ReviceMsgList.Add(UserData); //加入收到的消息列表內。


 收收到服務端的消息的時候。將消息頭部 轉換成如下消息格式:

 TMsgHeader = packed record
    dwCode: LongWord;
    nSocket: Integer;
    wGSocketIdx: Word;
    wIdent: Word;
    wUserListIndex: Integer;
    nLength: Integer;
  end;
記錄了消息的類型 長度 和Socket句柄等消息。根據wIdent的值

          case pMsg.wIdent of
            GM_CHECKSERVER: begin //客戶端需要先驗證網關
                boCheckServerFail := False;
                dwCheckServerTimeMin := GetTickCount - dwCheckServerTick;
                if dwCheckServerTimeMin > dwCheckServerTimeMax then
                  dwCheckServerTimeMax := dwCheckServerTimeMin;
                //dwCheckServerTick := GetTickCount();
              end;
            GM_SERVERUSERINDEX: begin //wGSocketIdx 爲服務端的Socket句柄 這句主要是修改wUserListIndex。暫時沒看出要幹嘛
                if (pMsg.wGSocketIdx < RUNATEMAXSESSION) and (pMsg.nSocket =
                  SessionArray[pMsg.wGSocketIdx].nSckHandle) then begin
                  SessionArray[pMsg.wGSocketIdx].nUserListIndex := pMsg.wUserListIndex;
                end;
              end;
            GM_RECEIVE_OK: begin
                {dwCheckServerTimeMin := GetTickCount - dwCheckRecviceTick;
                if dwCheckServerTimeMin > dwCheckServerTimeMax then dwCheckServerTimeMax := dwCheckServerTimeMin;
                dwCheckRecviceTick := GetTickCount(); }
                SendServerMsg(GM_RECEIVE_OK, 0, 0, 0, 0, nil); //消息到達處理,貌似很沒必要吧。應該是M2會做一些處理
              end;
            GM_DATA: begin
                ProcessMakeSocketStr(pMsg.nSocket, pMsg.wGSocketIdx, MsgBuff, pMsg.nLength);//進行編碼處理後 加入到每個會話的列表(實際是string)。此時並不是發送,並將消息加入SendMsgList;
              end;
            GM_TEST: begin

              end;


  DecodeTimerTimer 是網關處理數據的主循環。

      首先此函數會更新界面顯示,(這個耦合性也太大了吧。業務邏輯 跟界面扯上了關係)。

          然後將ReviceMsgList內所有的收到的消息發送給M2處理。(客戶端發送過來的)

         接着將SendMsgList 內所有的消息 投遞到客戶端(每個消息包括了客戶端描述 所以可以知道應該投給哪個客戶端)。
          然後每2秒向M2發送一個保活包。


總結:  網關收到客戶端的包 是直接轉發給M2的其中並沒有解密。而M2發送給客戶端的消息是要經過網關再EncodeMessage。也就是說網關並沒有承擔起完整的加解密。

              M2還要負責客戶端的解密操作。可以看出傳奇的源碼在設計上不太理想。


思考: Srv處理應該主要集中在遊戲數據的處理上 而封包的加密解密 應該給遊戲網關進行。同時遊戲網關內,不宜開多線程進行操作。因爲遊戲網關面對的資源是整個計算機的              資源。過多的線程可以讓單個網關獲得比較好的效率。但是遊戲網關在設計中並不是讓自身獲得更好的效率 而是讓整個計算機的資源充分的利用。如果自身一個網關無                法處理用戶請求的時候,應該開啓多個網關協同處理。 所以在網關的設計上應該將發送和接受 編碼解碼 放在同一個線程內。以多進程取代多線程,以避免在多線程處理             因爲生產者消費者模型 而導致兩個線程實際只有一個線程在工作。  個人愚見  錯誤請指正。




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章