1. 程式人生 > >小白探究UE4網路系列(一)、UE4網路基礎類分析

小白探究UE4網路系列(一)、UE4網路基礎類分析

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

一、概要   

  搗鼓UE4也有兩個多月了,從這兒開始,逐步探究UE4中經典的值複製,RPC兩種同步方式。想要弄到其複製和呼叫的原理,就得從根本的網路層開始捋,優秀的文章有:      

風蝕之月-UE4網路底層概覽-https://blog.ch-wind.com/ue4-network-overview/      

知乎專欄-Exploring In UE4- https://zhuanlan.zhihu.com/p/34723199      

  各位如果想大概瞭解網路的基本流程,可以認真學習一下這兩篇文章,深入淺出講解的很明晰。        

  我也是在拜讀了這幾篇文章後,下定認真檢視UE4的原始碼,從根本上了解UE4的網路原理,實現方案,優化方案等。  

  整個系列應該比較長,就跟著自己的學習筆記,慢慢探究UE4的網路設計和實現,當作一個別樣的旅程    

二、基本網路類     

   在遊戲開發中,基本的網路都是通用的,來自於Socket, 在UE中,使用的是其子類BSDSocket(伯克利套接字),而對於BSDSocket的使用,又是封裝在SocketSubSystem中,又在其基礎上封裝了一層SocketSubSystemModule來做模組管理。

  所以簡單的總結,其基本的層次為:     

    Socket->BSDSocket->SocketSubSystem->SocketSubSystemModule    

  那麼本文就介紹一下其基本的網路類,為後面的網路連線過程提供一個基本的知識印象。       

2.1 基本網路類 Socket

   這個類是最基本的類,自然就是和網路最底層掛鉤:IP 和 Port,主要用於實現對IP 和 port的連線,用於網路連線的繫結,監聽,資料收發。

2.1.1 基本介面    

  構建介面和解構函式:    

	inline FSocket() :
		SocketType(SOCKTYPE_Unknown),
		SocketDescription(TEXT("")),
		IsNotSendEncrypt(false),
		IsNotRecvDecrypt(false)
	{ }

  其中SocketType分為三類: None/TCP/UDP, 多一嘴,UE是以UDP為主的。IsNotSendEncrypt/IsNotRecvDecrypt 為收發加密相關。     

2.1.2 繫結相關介面

virtual bool Bind(const FInternetAddr& Addr) = 0;

virtual bool Connect(const FInternetAddr& Addr) = 0;

virtual bool Close() = 0;

2.1.3 資料接受相關介面   

virtual bool SetReceiveBufferSize(int32 Size, int32& NewSize) = 0;

virtual bool Recv(uint8* Data, int32 BufferSize, int32& BytesRead, ESocketReceiveFlags::Type Flags = ESocketReceiveFlags::None)   

virtual bool RecvFrom(uint8* Data, int32 BufferSize, int32& BytesRead, FInternetAddr& Source, ESocketReceiveFlags::Type Flags = ESocketReceiveFlags::None)   

2.1.4 資料傳送相關介面   

virtual bool SetSendBufferSize(int32 Size, int32& NewSize) = 0;

virtual bool SendTo(const uint8* Data, int32 Count, int32& BytesSent, const FInternetAddr& Destination);   

virtual bool Send(const uint8* Data, int32 Count, int32& BytesSent);  

   總結:這個類就是一個基類,大部分實現還是要看子類的override實現。

2.2 基本網路類 BSDSocket

   繼承自Socket的類,相關介面實現更為完備,類似的類為FSocketBSDIPv6, 就在這兒一起處理了。   

2.2.1 基本構造和析構介面   

	FSocketBSD(SOCKET InSocket, ESocketType InSocketType, const FString& InSocketDescription, ISocketSubsystem * InSubsystem) 
		: FSocket(InSocketType, InSocketDescription)
		, Socket(InSocket)
		, LastActivityTime(0)
		, SocketSubsystem(InSubsystem)
        , SendEncryptBufLength(DEFAULT_CRYPT_BUFFER_LEN)
        , RecvEncryptBufLength(DEFAULT_CRYPT_BUFFER_LEN)
	{ 
        SendEncryptBuf = new char[DEFAULT_CRYPT_BUFFER_LEN];
        RecvEncryptBuf = new char[DEFAULT_CRYPT_BUFFER_LEN];
    }

	/**
	 * Destructor.
	 *
	 * Closes the socket if it is still open
	 */
	virtual ~FSocketBSD()
	{
		Close();

        delete[] SendEncryptBuf;
        SendEncryptBuf = nullptr;
        delete[] RecvEncryptBuf;
        RecvEncryptBuf = nullptr;
	}

  比較簡單,相對父類而言,多了收發buff的構建和析構回收操作。     

2.2.2 其他介面

 

   也就是場景的資料收發,Bind, 連線,監聽,Wait幾個基本操作,也提供了多路操作。相對而言,重點關注幾個變數:     

   *  UpdateActivity() 這個函式用於更新最新的存活時間戳,應該是用於心跳檢測相關的;  

  *  FDateTime LastActivityTime: 最新的存活時間,用於心跳檢測,同上;

  *  ISocketSubsystem* SocketSubsytem 註釋中就說明了,只想建立其的SocketSubSystem, 所以BSDSocket的建立來自於SocketSubsystem的觸發,後面會分析到。    

2.2.3 具體的實現   

  在對於的cpp中會有很多具體的實現,我就重點歸納幾個要點:    

  • Close: 內部會呼叫底層的closesocket來關閉socket
  • Bind: 內部會呼叫底層的bind來Bind socket
  • Connect: 同上,內部會呼叫底層的connect來connect socket
  • Listen: 呼叫底層的listen介面,返回bool表示是否在監聽
  • SendTo: 如果傳送的資料超過當前的buffer,會刪除當前Buffer, 擴大2倍用來儲存和加密資料, 然後執行傳送操作sendto,如果傳送資料量超過0,就預設為傳送成功,重新整理LastActivityTime
  • Send: 同上,只是呼叫的介面為send
  • RecvFrom: 接收介面,如果接收資料size超過當前Buffer,會刪除當前buffer,擴大2倍用來存放接收的資料,對於接收到的資料,會進行是否接收數量(Byte)小於0且不為BLOCK狀態,如果是則說明當前接收的資料不正確,返回。對於正確接受的資料,會進行解密操作,存放在對應Buffer中, 重新整理LastActivityTime
  • Recv: 同上,只是呼叫的是recv介面,上面呼叫的是recvfrom介面,重新整理LastActivityTime
  • GetConnectionState: 獲取當前Socket的狀態,根據LastActivityTime,如果當前連線正常,同時資料收發間隔超過5s,則判斷是否需要重新重新整理連結時間LastActivityTime,還是斷開連線

   總結:這是專案中具體使用的BSDSocket,重點關注收發資料介面。   

2.3 基本網路類 SocketSubsystem    

  前面介紹了Socket和BSDSocket,相當於介紹了工具,那麼如何使用這個工具?UE給我們將這個工具做了一層封裝,也就是SocketSubSystem,對於封裝好的介面,我們想要呼叫,一般不會直接去呼叫,而是包裝了一層,構建一個工具的管理器來進行相關的操作,避免和工具直接打交道。當然,UE還在SocketSubsystem的基礎上更進一步的封裝了Module來管理,後面會分析。    

  在實際的專案中,SocketSubsystem只是一個積累,具體的SocketSubsystem,還需要根據對於的平臺來進行建立,目前理出的主要的繼承關係如下:    

ISocketSubsystem---FSocketSubsystemBSDCommon---FSocketSubsystemBSD      ---FPS4SocketSubsystem
                                                                        ---FSwitchSocketSubsystem
                                                                        ---FSocketSubsystemWindows
                                                                        ---FSocketSubsystemLinux
                                                                        ---FSocketSubsystemMac
                                                                        ---FSocketSubsystemAndroid
                                                                        
                                            ---FSocketSubsystemBSDIPv6  ---FSocketSubsystemIOS
                                                                        ---FSocketSubsystemAndroidv6
                ---FSocketSubsystemSteam
                ---FSocketSubsystemHTML5

   基本關係:在遊戲啟動的時候,會先載入SocketSubsystemModule, 然後觸發對應平臺的SocketSubsystem的建立,再執行對應的BSDSocket的建立,所以接下來看看SocketSubsystemModule的載入過程。     

2.4 基本網路類 SocketSubsystemModule的載入

   在遊戲啟動,或者編輯器啟動的時候(注意編輯器下不是執行play,而是編輯器啟動的時候),會執行Module的載入。在UE4中,各個模組都會統一到Module設計中,對應於網路這部分,就是SocketSubsystemModule,下面來逐步跟隨追蹤其載入過程: 

2.4.1 載入FOnlineSubsystemModule   

  首先會執行載入FOnlineSubsystemModule,在執行完該Module的載入後,會執行一次StartupModule(Module的虛擬函式介面,每個Module各自實現),在FOnlineSubsystemModule中實現為:        

   在載入完成後,會執行GetOnlineSubsystem的操作:      

   第一次載入的時候,OSSFactory為空,則會觸發執行CreateSubsystem的操作:   

  

   會在FOnlineFactoryNull中執行建立FOnlineSubsystemNull,然後執行初始化:    

   初始化的關鍵是: new FOnlineIdentityNull的建立:     

   裡面會執行Login,而Login會進入到ISocketSubsystem:Get操作:   

   最終來到了建立靜態SocketSubsystem的操作:   

   靜態第一次建立的時候,執行載入SocketSubsystemModule,則其會執行:     

   對應編輯器為Windows,則會執行:   

   建立完後的register:   

   總結:至此,完成了從FOnlineSubsystemModule的初始化,到SocketSubsystemModule的載入,然後完成SocketSubsystem的建立和註冊的流程。

本文就寫到這兒,主要分析了基本的網路類,網路system以及相關Module的載入,下一篇會重點分析NetDriver 和Connection 這兩個UE中的重頭類。

&n