亲宝软件园·资讯

展开

探究UE4网络系列(二)、UE4网络核心类分析

zblade 人气:0

转载请标明出处: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和值复制的逻辑原理两个方面继续深入分析网络连接。

 

加载全部内容

相关教程
猜你喜欢
用户评论