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

探究UE4网络系列(二)、UE4网络核心类分析的更多相关文章

  1. Tomcat 学习进阶历程之Tomcat架构与核心类分析

    前面的http及socket两部分内容,主要是为了后面看Tomcat源代码而学习的一些网络基础.从这章開始.就開始实际深入到Tomcat的'内在'去看一看. 在分析Tomcat的源代码之前,准备先看一 ...

  2. Spring5源码解析系列一——IoC容器核心类图

    基本概念梳理 IoC(Inversion of Control,控制反转)就是把原来代码里需要实现的对象创建.依赖,反转给容器来帮忙实现.我们需要创建一个容器,同时需要一种描述来让容器知道要创建的对象 ...

  3. keystone系列二:keystone源码分析

    六 keystone架构 6.1 Keystone API Keystone API与Openstack其他服务的API类似,也是基于ReSTFul HTTP实现的. Keystone API划分为A ...

  4. disruptor笔记之二:Disruptor类分析

    欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...

  5. Android系列之网络(二)----HTTP请求头与响应头

    ​[声明] 欢迎转载,但请保留文章原始出处→_→ 生命壹号:http://www.cnblogs.com/smyhvae/ 文章来源:http://www.cnblogs.com/smyhvae/p/ ...

  6. openstack-- neutron 二/三层网络实现探究

    引出 Neutron 是openstack 中提供网络虚拟化的组件,根据二层网络的实现方式不同(即agent的不同),可以分为Linux bridge的方式,Openvswitch的方式.而且,lay ...

  7. Alamofire源码解读系列(七)之网络监控(NetworkReachabilityManager)

    Alamofire源码解读系列(七)之网络监控(NetworkReachabilityManager) 本篇主要讲解iOS开发中的网络监控 前言 在开发中,有时候我们需要获取这些信息: 手机是否联网 ...

  8. 百度APP移动端网络深度优化实践分享(二):网络连接优化篇

    本文由百度技术团队“蔡锐”原创发表于“百度App技术”公众号,原题为<百度App网络深度优化系列<二>连接优化>,感谢原作者的无私分享. 一.前言 在<百度APP移动端网 ...

  9. Windows Server 2012 虚拟化实战:网络(二)

    关于Windows Server的虚拟化网络,前文描述了在操作系统层面上的出现的配置变化.其中的一些配置通过Windows Server提供的小工具即可实现,如网卡组的配置,而有些需要安装Window ...

随机推荐

  1. 【Java】面向对象之继承

    多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继承那一个类即可.其中如图中所示,食草动物.食肉动物.兔子.羊.狮子.豹都可以称为子类,动物类称为父 ...

  2. LVM扩容案例

    LVM基础命令: pvdisplay 查看检查pv pvremove /dev/sdb #清除一个pv fdisk -l 检查磁盘 df -h 检查全部磁盘大小 df -Th 检查磁盘大小和分区格式类 ...

  3. 新闻实时分析系统-MySQL安装

    1.修改yum源 鉴于用国外的Yum源,速度比较慢,所以想到将国外的yum源改为国内的Yum源,这里选择使用比较多的阿里云源.具体修改方法可以参考此连接 2.在线安装mysql 通过yum在线mysq ...

  4. 教你用Java web实现多条件过滤功能

    生活中,当你闲暇之余浏览资讯的时候,当你搜索资料但繁杂信息夹杂时候,你就会想,如何更为准确的定位需求信息.今天就为你带来: 分页查询 需求分析:在列表页面中,显示指定条数的数据,通过翻页按钮完成首页/ ...

  5. Stream系列(七)distinct方法使用

    EmployeeTestCase.java package com.example.demo; import lombok.Data; import lombok.ToString; import l ...

  6. echarts对柱状图进行标注,以及取消hover时的阴影

    option = { color: ['#3398DB'], tooltip : { trigger: 'axis', axisPointer : { // 坐标轴指示器,坐标轴触发有效 type : ...

  7. ctf中关于图片的隐写随笔(不全)

    ①JPG图片的结束符,十六进制常为FFD9 ②binwalk的原理是:检查常见的文件头信息,如果不符合,一定有隐藏信息.③JPG是有损压缩,PNG是无损压缩,BMP是不压缩. 隐写的基本原理:图片查看 ...

  8. java Random类详解

    java Random类位于java.util包下,主要用来生成随机数,本文详解介绍了Random类的用法,希望能帮到大家 Random类 (java.util) Random类中实现的随机算法是伪随 ...

  9. __getattribute__(self, obj) 这个方法中的obj这个参数

    class Itcast(object): def __init__(self, subject1): self.subject1 = subject1 print("^^^^^^^---- ...

  10. php方法注释

    注释格式 <?php /** * @method 发送邮件 * @url email/send?token=xxx * @http POST * @param token string [必填] ...