小白探究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