1. 程式人生 > >探究UE4網路系列(二)、UE4網路核心類分析

探究UE4網路系列(二)、UE4網路核心類分析

轉載請標明出處:http://www.cnblogs.com/zblade/

一、概要

前面分析了網路核心的基礎類Socket/BSDSocket/SocketSubsystem/SocketSubsysteModule,其主要是用於基礎的模組載入和socket連線。在此基礎上,進一步的層次是NetDriver和Connection, 這兩者並不是完全上下層的關係,更像是互相依託的關係。在實際的應用中會在NetDriver的基礎上進一步封裝出子類IpNetDriver。

所以本文就主要分析三大類:NetDriver/IpNetDriver/Connection。NetDriver還有個子類DemoNetDriver,主要用於replay,這兒就不再詳細分析,需要的可以去深入研究一下。   

二、三大基礎網路核心類

2.1 基礎類 NetDriver    

在每個單獨的UE程序中,只會有一個NetDriver的例項,雖然具體的是UIpNetDriver,但是本質是一樣,都是網路驅動類。NetDriver和NetConnection是互相依託的,簡單的來說,就是構建NetDriver,然後註冊NetConnection,在NetConnection中註冊ActorChannel, ControlChannel,VoiceChannel等。在更新的時候,從World觸發相關Tick,然後觸發到NetDriver,然後逐個觸發其上面的Connection。在NetDriver和NetConnection中會有一些和RPC相關的操作介面,當然RPC的主要操作還是在UChannel上,這個在後續文章會具體分析其原理。    

2.1.1 主要變數   

NetDriver的主要變數有:      

  • class UNetConnection* ServerConnection: 連線到的伺服器Connnection,只在客戶端才賦值
  • TArray<class UNetConnection*> ClientConnections: 連線到host的所有client connection, 這在host上才維護,注意這是一個數組,也就是可以有多個客戶端在host上註冊
  • TUniquePtr<PacketHandler> ConnectionlessHandler: 在連線初始化的時候會應用到
  • TWeakPtr<StatelessConnectHandlerComponent> StatelessConnectComponent: 在連線初始化的時候會用到
  • class UWorld* World: 當前NetDriver註冊所在的World
  • TMap< TWeakObjectPtr< UObject >, TSharedPtr< FRepChangedPropertyTracker > > RepChangedPropertyTrackerMap; RPC相關的歷史儲存資料變數
  • TMap< TWeakObjectPtr< UObject >, TSharedPtr< FRepLayout > >RepLayoutMap; RPC相關的資料變數
  • TMap< TWeakObjectPtr< UObject >, TSharedPtr< FReplicationChangelistMgr > >ReplicationChangeListMap; RPC相關的資料變數

2.1.2 初始化和建構函式

建構函式主要是對一系列的變數進行賦值操作,然後對Control/Actor/Voices三種Channel的類進行註冊。 完成構造後會有幾個相關的初始化介面:   

ENGINE_API virtual void PostInitProperties() override;
主要是初始化simulation相關屬性,然後確定是否有timeout,然後註冊level的載入的兩個事件委託

ENGINE_API virtual void FinishDestroy() override;   
銷燬操作,清理伺服器和客戶端connection, 然後移除上面註冊的兩個事件委託

ENGINE_API virtual void Serialize( FArchive& Ar ) override;    
序列化相關

ENGINE_API static void AddReferencedObjects(UObject* InThis, FReferenceCollector& Collector);   
新增到gc物件列表中,然後從當前replication相關資料中移除  

其他和構建銷燬相關的介面有:    

InitBase:
  Common initialization between server and client connection setup
InitClient:
  Initialize the net driver in client mode
InitListen:
  Initialize the network driver in server mode (listener)
InitConnectionlessHandler:
  Initialize a PacketHandler for serverside net drivers, for handling connectionless packets
FlushHandler:
  Flushes all packets queued by the connectionless PacketHandler
InitConnectionClass:
  Initializes the net connection class to use for new connections
ShutDown:
  Shutdown all connections managed by this net driver
LowLevelDestroy:
  Close socket and Free the memory the OS allocated for this socket

2.1.3 RPC相關的介面

 RPC的三個觸發相關的介面:   

  • TickDispatch:   handle time update,tick更新前的time更新
  • TickFlush:   ReplicateActors and Flush, 值賦值和重新整理
  • PostTickFlush: PostTick actions tick更新後的重新整理和清除操作

 具體的RPC相關的介面:   

  • PreCheckReplicateActors: Replicate之前的操作
  • FlushActorDormancy: 重新整理處於睡眠狀態的Actor
  • ForceActorRelevantNextUpdate: 強制重新整理actor的關聯關係
  • PreReplicateActors: actor進行值複製之前的操作
  • ServerReplicateActorsXXX: 以ServerReplicateActors開頭的函式介面,都是伺服器執行值複製的相關介面
  • ProcessRemoteFunction: 處理RPC函式呼叫的介面    

 2.1.4 網路相關的介面  

NetDriver本身還是又和資料傳送相關的介面,主要是:    

LowLevelSend: 資料傳送介面
ProcessLocalServerPackets: Process any local talker packets that need to be sent to clients
ProcessLocalClientPackets: Process any local talker packets that need to be sent to the server

 總結: NetDriver其實主要是初始化,RPC和值複製相關介面,對於值複製相關的介面,在後續的值複製操作中會詳細的分析,這兒就不展開詳細分析。

2.2 基礎類 IpNetDriver

相比於其父類NetDriver,IpNetDriver主要負責具體的Tick和Socket的建立,所以整體的網路最終網路連線例項實在這兒進行的,當然部分還是沿用其父類的實現。

2.2.1 主要變數  

  • FSocket* Socket: 當前IPNetDriver關聯建立的Socket
  • uint32 ServerDesiredSocketReceiveBufferBytes :Number of bytes that will be passed to FSocket::SetReceiveBufferSize when initializing a server
  • uint32 ServerDesiredSocketSendBufferBytes: Number of bytes that will be passed to FSocket::SetSendBufferSize when initializing a server.
  • uint32 ClientDesiredSocketReceiveBufferBytes: Number of bytes that will be passed to FSocket::SetReceiveBufferSize when initializing a client
  • uint32 ClientDesiredSocketSendBufferBytes :Number of bytes that will be passed to FSocket::SetSendBufferSize when initializing a client.

2.2.2 主要介面

1. InitBase

在host端,將connectionlesshandler重置為null,為後續的登入連線做準備。   

2. InitConnect    

 在呼叫InitBase後,會對應的建立一個ServerConnection,然後初始化,並在這個ServerConnection中建立一個Control Channel,其實現為:    

這個Control Channel會在後面的連線過程中被使用。    

3. InitListen

初始化話監聽相關,同時處理ConnectionlessHandler相關:   

 

4. LowlevelSend

 資料傳送,具體看實現的cpp程式碼即可。

5. TickDispatch   

每幀的更新,看具體的實現即可。

6. ProcessRemoteFunction   

對RPC函式的操作,後面會詳細的分析。

總結:IpNetDriver雖然作為具體的網路驅動類,其部分邏輯沿用父類,部分自我實現,具體的分析,最好還是和後面的RPC和網路連線一起分析比較好,這兒就忽略不具體分析,後面來具體補充分析介面的實現邏輯和流程。

 

2.3 基礎類 NetConnection   

上面的兩個類只是簡單的闡述其介面和變數,由於其作為網路連線的核心驅動類,在後面的網路連線初始化,值複製和RPC中會反覆的提及和分析,所以就簡單的分析了。NetConnection作為連線類,在資料收發和網路狀態中具有重要的作用,所以先詳細的分析這個類。

2.3.1 基本變數分析

NetConnection中的變數,主體還是和網路連線相關,主要可以分為以下幾類:    

1. 連線相關外部類  

  • class UNetDriver* Driver: 該connection所關聯的NetDriver
  • class AActor* ViewTarget: 當前connection關聯的actor
  • class AActor* OwningActor: 主要指當前connection所關聯的controller(controller本身也是actor)

2. 網路配置相關資料

  • int32 MaxPacket; 最大packet size
  • int NumPacketIdBits; 當前packet中PacketId所佔bit數目
  • int NumBunchBits: 當前packet中的bunches所佔bit數目
  • int NumAckBits: 當前packet中ack所佔bit數目
  • int NumPaddingBits: 當前packet中Padding所佔bit數目
  • int32 MaxPacketHandlerBits: the maximum number of bits all packet handlers will reserve
  • enum{ MAX_CHANNELS = 10240 }; 最大的channel數目,當然每個專案可以自己修改數目,現在10240對於有的有點過大

3. 網路連線相關

  • EConnectionState State       當前網路連線狀態:Invalid/Closed/Pending/Open
  • uint32 bPendingDestroy:1;        true的時候,表明當前controller或則client正在被銷燬,標誌位
  • TUniquePtr<PacketHandler> Handler:     PacketHandler,主要用來管理packets的收發
  • TWeakPtr<StatelessConnectHandlerComponent> StatelessConnectComponent:          主要用來指向PacketHandler component, 用來管理無狀態連線的握手處理,後面在網路初始化的時候會分析講解
  • bool bNeedsByteSwapping:      當前資料是否需要交換byte
  • FUniqueNetIdRepl PlayerId:     這個id只在客戶端有效,在server端儲存這個id,用來表明remote連線的client

其餘還有連線時的各種資料,主要分為連線時的資料,連線時的時間變數,以及網路stat相關資料,這兒就不再具體分析了。

4. Packet相關   

  • FBitWriter SendBuffer: 傳送的buffer, queued up bits waiting to send
  • double OutLagTime[256]: for lag measuring
  • int32 OutLagPacketId[256]: for lag measuring 延遲優化相關
  • int32 OutBytesPerSecondHistory[256]: for saturation measuring
  • float RemoteSaturation:
  • int32 InPacketId: full incoming packet index
  • int32 OutPacketId: most recently sent packet
  • int32 OutAckPacketId: most recently acked outgoing packet

5. Channel Table相關

  • class UChannel* Channels[MAX_CHANNELS];
  • int32 OutReliable[MAX_CHANNELS];
  • int32 InReliable[MAX_CHANNELS];
  • int32 PendingOutRec[MAX_CHANNELS];
  • TArray<int32> QueuedAcks, ResendAcks;

6. RPC相關變數

  • TMap<TWeakObjectPtr<AActor>, UActorChannel*, FDefaultSetAllocator, TWeakObjectPtrMapKeyFuncs<TWeakObjectPtr<AActor>, UActorChannel*>> ActorChannels; 簡單明瞭,每個actor一個channel
  • TMap<FNetworkGUID, TArray<class UActorChannel*>> KeepProcessingActorChannelBunchesMap: This holds a list of actor channels that want to fully shutdown, but need to continue processing bunches before doing so
  • TMap< TWeakObjectPtr< UObject >, TSharedRef< FObjectReplicator > > DormantReplicatorMap 睡眠actor列表
  • TSet<FNetworkGUID> DestroyedStartupOrDormantActors

2.3.1 基本介面分析

主要分為以下幾種主要介面: 

 1. 構造相關的介面

InitBase介面
初始化該Connection的例項基本設定,主要為設定stat相關的初始時間,設定handler,建立packagemap, 以及建立一個voice channel, 不過voice channel 基本現在不常用,相關介面可以暫時不關注

InitHandler
在上面介面中會執行InitHandler, 會執行MakeUnique來建立一個Handler,然後設定其mode為client還是server,初始化委託,並建立statelessconnectcomponent,為後面的握手操作設定。

InitConnection
這個相對於InitBase,主要沒有Handler的設定相關,stat的設定相關,這個主要用於DemoNetDriver中的設定,DemoNetDriver主要用於回放系統

InitSequence
在握手完成後基於握手的資料重新設定InComingSequence/OutgoingSequence

EnableEncrytionWithKeyServer/EnableEncryptionWithKey
設定加密的key

Close
會將所有的Channel關閉,然後執行一次FlushNet

CleanUp
對所有子NetConnection執行CleanUp, 然後執行Close, 然後區分當前connection為server還是client,分別執行對應的connection的置空和remove操作,最後當前connection上的所有openchannel/actorchannel執行cleanup, 置空handler和Driver等一系列置空清除操作

FinishDestroy
呼叫CleanUp

AddReferencedObjects
將當前物件新增到可GC列表中

2. 資料接收相關介面   

ClientHasInitializedLevelFor
returns whether the client has initialized the level required for the given object

ValidateSendBuffer:專案改進的介面
當前sendbuffer是否有效

InitSendBuffer
初始SendBuffer

ReceivedRawPacket(void InData, int32 Count)*
接收到rawpacket,重點介面,如果handler不為空,則執行InComming的資料接收到的校驗,然後重新整理接收到的資料的統計,然後構建FBitReader,執行ReceivedPacket

ReceivedNak
獲取到重新發送的packet的操作, 其來在receivedpack

ReceivedPacket
重點介面,獲取到packet的相關操作,主要有

  * 更新時間戳
  * 檢測packet的packetId的合法性,只能遞增,不能比當前packet更小,否則就被標記為亂序不執行後續
  * 分類執行,基於當前packet是否為ack進行區分執行
  * Ack的packet的執行分類
    * 判斷當前packet是否為重發的Ack, 是則呼叫上面的ReceivedNak
    * 更新controller的Ping
    * 更新當前OpenChannels中的每個channel的ack狀態
  * 非Ack的packet的執行分類
    * 將當前packet解析為bunch
    * 對bunch進行排序
    * 基於bunch的ChIndex進行校驗,對應不存在的情況進行校驗,如果不存在當前對應的channel,同時Control對應的channel沒有建立,則返回
    * 如果control對應的channel存在,並且bunch對應的ChIndex對應的channel不存在,則執行對應的channel的建立
    * 在建立完channel後進行是否接收該建立的channel的校驗,如果不通過,則close和delete該channel
    * 如果通過校驗,則設定執行ReceivedRawBunch
    * 重新整理計數,校驗Bunch
  * 傳送回ack packet: SendAck

3. 資料傳送相關

WriteBitsToSendBuffer
主要是建立SendBuffer對應的FBitWriterMark,然後序列化資料進去,具體傳送的時機,要麼是當前操作完成後,已經填滿buffer,則觸發flushnet,否則等待下一次flushnet的時候執行LowLevelSend才會執行send

SendAck
傳送Ack包,只限制在非replay得情況下,對Ack對應得FBitWriter進行資料填充和序列化,然後呼叫WriteBitsToSendBuffer

SendRawBunch 對channel中發來的Bunch資料再進行包裝一層後傳送出去,呼叫WriteBitsToSendBuffer

4. Tick

Tick的基本操作可以分為幾個部分:

  • 基本時間重新整理:主要是各種stat的時間,更新的時間,以及超時的判斷
  • 如果超時,則執行Close操作
  • 如果沒有超時,則執行更新,主要更新:集中刷新發送Ack資料包
    • ChannelsToTick陣列中元素的更新
    • OpenChannels的元素更新
    • KeepProcessingActorChannelBunchesMap字典中value的更新
  • 處理連線過程中的FlushNet
  • 集中處理刷新發送Handler中的rawpacket/packet資料包
  • 做一個數據統計

所以基本的還是資料統計,channel更新,handler中packet的相關刷新發送

5. FlushNet

FlushNet,相當於Flush操作,將當前快取積累的資料都進行一次flush操作。函式操作比較長,但是主體的操作就是:將當前的sendbuffer中的資料執行 LowLevelSend操作來發送出去,然後重新整理對應的相關計數和統計資料

6. RPC相關

NetConnection不是RPC的主要戰場,會有部分與RPC相關的介面,主要是對Dormancy的actor進行重新整理的操作:

FlushDormancy
對當前actor以及actor的cpnt進行FlushDormancyForObject的操作

FlushDormancyForObject
對當前DormantReplicatorMap字典中的物件進行dormancy state的判斷,為後面的replication操作做準備

7. 登入相關

SetClientLoginState
設定客戶端的loginstate

SetExpectedClientLoginMsgType
在伺服器上設定期望下次客戶端在登陸時候傳送的msg type

IsClientMsgTypeValid
和上面對應的msg type進行比對

總結: 其實從介面分析來看,NetConnection主要的就是構造初始化,斷開相關,對Packet資料進行收發相關,以及和登入RPC部分相關的介面,具體RPC的操作,還是需要在對應的Channel中進行仔細推敲分析,後面回進一步的分析到RPC相關。

 

在完成基本類的分析後,後面還會有對UChannel這個類的詳細分析,但是其最好是和值複製以及RPC具體掛鉤分析更佳。下面會分為網路連線的流程,RPC和值複製的邏輯原理兩個方面繼續深入分析網路連線。

&n