ET框架學習——消息系統之七,從init走一遍網絡消息

  • 網絡組件創建       

       ET中的網路組件在客戶端就是NetOuterComponent,當然,這是包裝過的。這裏面只是指定了所使用的網絡類型,demo中默認使用的是TCP類型的網絡。這裏使用的組件工廠添加組件,所以會默認執行Awake事件。

       NetOuterComponent組件的Awake事件是定義在類NetOuterComponentAwakeSystem中的,值得注意的是,這裏還有一個NetOuterComponentAwakeSystem1類,但是該類重寫的Awake方法有兩個參數。Awake方法會執行NetOuterComponent組件的Awake方法,同時賦值兩個屬性MessagePackerMessageDispatcher

      NetOuterComponent組件繼承自NetworkComponent組件。NetworkComponent組件的Awake方法會根據選擇的NetworkProtocol類型不同,創建不同的AServer類型,如圖:

如圖:

到這裏呢,網絡組件創建完畢。

  • 網絡組件的使用

       網絡組件的使用包括收發消息,協議的定義等兩部分。這一部分是難點,畢竟我沒有做過網絡開發(手動哭泣)。 這裏使用的是鬥地主的demo,在玩家登陸的過程中是會有消息的發送和返回的,可以從這裏開始看看網絡組件的使用。

       面板部分有可能是寫在hotfix中的。hotfix的啓動是利用LoadHotfixAssembly方法加載了hotfix的dll,然後使用GotoHotfix方法進入了hotfix的主入口函數。跟蹤代碼,可以看到在HotFix.UIComponent中使用Create方法創建了登錄UI界面。

這裏,我們來詳細分析一下這個過程,點擊註冊按鈕後

        1、通過NetOuterComponent組件,創建一個Model層的Session對象。

        2、再創建一個Hotfix.Session對象,注意,這裏是通過ComponentFactory創建的。並且,Hotfix.Session是和Model.Session有關聯的。Hotfix.Session會通過Model.Session發送消息。

        3、通過Call方法向服務器發送創角的協議請求。

        4、檢查協議對象返回的錯誤碼,確認是否註冊成功。成功後,調用登錄方法(登錄按鈕觸發)。

        5、斷開連接。

下面來詳細看看Call方法中做了什麼。首先呢,創建Hotfix.Session後,會執行Awake方法,該方法會將Model.Session對象傳遞進Hotfix.Session中,同時會在其下面掛載一個SessionCallbackComponent組件,設置該組件中的兩個Action委託:

Action委託:

 在Hotfix.Session.Call方法中,添加元素到requestCallback字典中去,Key值是一個自增變量,Value值是一個Action委託,在檢查錯誤碼後,會把委託設置到線程中去。讓後將協議的RpcId屬性設置爲剛剛的自增變量值,這樣,就可以通過該值,將協議和委託相對應。調用Hotfix.Session.Send(byte flag, IMessage message)方法,最後返回一個Task對象。

       在Hotfix.Session.Send(byte flag, IMessage message)方法中,會獲取到Model.OpcodeTypeComponent組件,通過該組件獲取到協議對象的opcode值。該值定義在HotfixOpcode文件中,同時爲對應的協議類,設置了標籤。總結起來就是,協議類持標籤,該標籤內部有一個opcode屬性,在設置標籤的時候,會定義該值,該值則定義在HotfixOpcode文件中。這樣就可以通過反射,事先收集所有的協議對象,並且和opcode值對應,緩存在Model.OpcodeTypeComponent組件組件中。最後再調用Model.Session.Send(byte flag, ushort opcode, IMessage message)方法,這裏除了協議對象參數,還有opcode值,以及一個flag值(具體還未知道用途)。

       Model.Session.Send(byte flag, ushort opcode, IMessage message)方法中,會使用流來發送協議對象。這裏面會對協議對象進行進一步的包裝(設置包頭等等)。值得注意的一點是,這裏的流對象其實是對應的Model.TChannel對象(因爲選擇的是TCP連接)中的流對象。

       通過調試,可以發現,寫入流的過程如下:

       1、在流當前位置,空出3個單位的位置後設置指針。

       2、設置流的長度爲3個單位(可能涉及到擴容)。

       3、通過調用ProtobufHelper.ToStream方法,將協議內容寫入流。根據調試,可以發現,寫入流的方法其實是寫在協議類中的。

       4、最後再設置下流的指針到寫入的數據的末尾。

       最後再設置byteses列表(兩個byte數組元素),將傳遞過來的flag參數設置成第一個byte數組的第一個元素。再把opcode值設置到第二個數組中去(通過位運算,將數組前兩個元素設置爲指定值)。

       接下來就是對數據進行包裝了,這個byteses列表就是一個處理的中轉站。將flag值放在了流的第一個位置,將opcode值放在了流的第二個位置。

       接着調用channel對象的Send(MemoryStream stream)方法。該方法中會設置緩存字節數組cache的前幾個元素,設置完成後,寫入到TChannel.sendBuffer對象頭部,相當於再一次進行包頭(這裏設置的,其實是協議數據的長度)。然後將流中的數據數據寫入到TChannel.sendBuffer對象中去(詳見這篇文章),最後調用TService.MarkNeedStartSend方法標記該TChannel對象是有數據待發送的。

       之後,回到主線程。執行TChannel.OnConnectComplete方法,進入StartRecv方法,確認是否連接成功。發送完成後,在主線程調用TChannel.OnSendComplete方法,確認是否發送完成。再次回到主線程,調用TChannel.OnRecvComplete方法。

       在TChannel.OnRecvComplete方法中,會從接受到的包中,解析出數據。解析出流數據後,執行Session.Run方法。在Run方法中,重新解析出flag,opcode和協議對象內容,其步驟和先前包裝數據時的步驟相反:

       1、將流的指針向後移動3個單位。

       2、獲取第一個位置的flag值和第二個位置的opcode值。

       判斷opcode值是否是Hotfix層的消息,是則執行SessionCallbackComponent.MessageCallback方法,該方法在創建Hotfix.Session後就進行了賦值。

       進入Hotfix.Session.Run方法後,就需要獲取協議內容了,

       1、通過OpcodeTypeComponent組件,根據opcode值,創建對應的協議對象。

       2、通過ProtobufPacker.DeserializeFrom方法,反序列化出數據。

       拿到數據後,根據拿到的flag值,將消息內容對象強轉爲IResponse對象,根據RpcId值,判斷requestCallback中有麼有對應的委託方法,並且執行該委託。所以,flag的目的,應該是判斷客戶端發過去的協議是否是需要返回的類型。而該委託定義在Hotfix.Session.Call方法中,判斷了一下錯誤碼,然後返回了一個Task<IResponse>對象,該對象其實就是協議的返回類型。

協議的返回類型。

 如果是不需要返回的協議,那麼會使用MessageDispatherComponent組件來處理,  關於該組件可以看文章《ET框架學習——OpcodeTypeComponent組件和MessageDispatherComponent組件》。使用Handle方法來分發該消息。 處理邏輯和之前需要返回的消息不同點在於:需要返回的協議的回調方法是通過RpcId來區分的,但是不需要返回的協議的回調方法則是通過opcode值區分的。

       到這裏,一個網絡請求協議的發送和接受就完成了。

       後面就是登錄環節,發送登錄協議,在收到登錄返回後,利用登錄地址創建gate服務器,然後登錄gate服務器,後面玩家與服務器的通訊都是通過該gate服務器轉發。

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