postman rpc本地调用_[UE4 Network] RPC 之哪里调用 和 哪里执行

47a5ccc0e4f27a46d6e0707ed54e29fa.png

区分调用 和 执行

对于由 Client , Server , NetMulticast 说明符声明的三种类型的 RPC 函数,要了解它们的使用规范首先我们要区分调用 和 执行概念的区别。

假设我们声明了一个名为 FunctionName() 的 RPC 函数,用户并不需要实现这个函数,而是要额外的实现一个名为 FunctionName_Implementation() 函数处理真正要执行逻辑。而这个 FunctionName() 函数会由 UHT 来为我们实现来处理 RPC 调用的逻辑。

我们将用户调用 FunctionName() 函数行为称为用户发起了这个 RPC 函数的调用,而将真正执行到 FunctionName_Implementation() 函数时称为这个 RPC 函数被执行了。


RPC 调用和执行的简单流程

发送端调用流程

  1. 假设由 UFUNCTION( Client ) 声明了一个名为 FunctionName() 的 RPC 函数。这个函数会由 UHT 实现,用户无需对它实现,但用户需要额外的实现一个 FunctionName_Implementation() 函数处理函数的真正执行逻辑。
    UHT 首先会生成一个名为 execFunctionName() 函数,由它来调用用户定义的 FunctionName_Implementation() 函数,并会以 "FunctionName" 为函数名,来将这个 execFunctionName() 函数注册为当前 UClass 类的一个 UFunction 对象。
    然后 UHT 会生成 FunctionName() 函数的实现,其内就是获取自己所属 UClass 中名为 "FunctionName" 的 UFunction 对象,并调用 UObject::ProcessEvent() 处理这个 UFunction 的执行逻辑。因此 RPC 函数如何被远程调用,就是由 UObject::ProcessEvent() 来实现的
  2. 在 UObject::ProcessEvent() 中首先会调用 AActor::GetFunctionCallspace() 来获取一个 FunctionCallspace 枚举来标识此 UFunction 的执行空间。其取值如下
    FunctionCallspace::Absorbed 终止执行,即不会本地执行也不会远程执行
    FunctionCallspace::Remote 远程执行,需要通过UNetDriver来远程调用
    FunctionCallspace::Local 本地执行,仅本地执行
    在获取了函数的执行空间后,若需要本地执行则本地执行,若还需要远程执行则参见 UObject::ProcessEvent() 会回调自己的 UObject::CallRemoteFunction() 来处理远程执行的逻辑。
    参见 AActor::CallRemoteFunction() 中,会调用UNetDriver::ProcessRemoteFunction() 来处理 RPC 远程调用。最终会调用 FRepLayout::SendPropertiesForRPC() 来将 RPC 调用实参封装成一个 RPC Bunch ,并通过 UActorChannel 通道发送给对端

接收端执行流程

  1. 在接收到的 Bunch 中通常会包含许多个属性块,来保存 AActor 和其子对象的同步属性。且每个对象都对应于一个 FObjectReplicator 对象来处理其属性块数据的解析。因此在通道的 UActorChannel::ProcessBunch() 中解析 Bunch 中每个属性块时,会获取每个属性块对应的 FObjectReplicator 对象,并调用其 FObjectReplicator::ReceivedBunch() 来解析此对象的属性。
  2. 在 FObjectReplicator::ReceivedBunch() 中若发现一个和 UFunction 相关的数据块,通常表示这是对端发送来的 RPC 调用的数据。此时会调用 FObjectReplicator::ReceivedRPC() 来处理这个 UFunction 的执行逻辑。大致如下。
    首先会先验证此 UFunction 是否为能在本机合法执行的 RPC 函数
    >>1 若本机为客户端,则被执行的需要为 UFUNCTION( Client ) 或 UFUNCTION( NetMulticast ) 修饰的函数 ,否则不能执行
    >>2 若本机为服务器,则被执行的需要为 UFUNCTION( Server ) 修饰的函数 ,且同时要求当前链接拥有这个 AActor 才能执行,否则不能执行
    在验证可以调用后,会获取这个 UFunction 对应的 FRepLayout
    并调用 FRepLayout::ReceivePropertiesForRPC() 解析 Bunch 中的 RPC 调用实参。
    然后会传入调用实参,并以当前正在解析的这个属性块所属的对象作为 this 来调用 UObject::ProcessEvent() 来执行这个 UFunction
    通常此函数会在接收端立即执行,参见 RPC 调用的一些细节 中 [ 可靠 RPC 在客户端的延迟执行 ] 的分析,在客户端调用一个可靠的 RPC 函数,且其包含未求解成功的对象引用类型的实参时,还可能发生延迟执行。

三种 RPC 函数

相关概念的简介

  1. 由 AActor->LocalRole == ROLE_Authority 检测是否为本地权威角色
  2. 由 AActor::GetNetConnection() != NULL 检测 AActor 是否有所属链接
  3. 由 AActor->RemoteRole != ROLE_None 检测 AActor 是否为参与属性同步

UFUNCTION( Server )

用于声明由客户端发起调用,在服务器执行的 RPC 函数。
无论是否可靠,构造的 RPC Bunch 都会由 UChannel::SendBunch() 立即发送。

执行空间
  1. 在服务器调用时仅本地执行
  2. 在客户端调用时,对于本地权威角色的 AActor ,若其参与属性同步且有所属链接则远程执行,否则仅本地执行
  3. 在客户端调用时,对于非本地权威角色的 AActor ,若其参与属性同步且有所属链接则远程执行,否则终止执行
远程执行的过滤情况
  1. 若是不可靠的 RPC 调用,且链接发送缓存已饱和,则客户端会抛弃此调用
  2. 若还未在链接中为 AActor 创建通道则抛弃调用,需要等服务器由 AActor 通道发送 OpenBunch 促使客户端创建通道后,客户端才能发起 RPC 调用
  3. 若服务器接收到这个 RPC Bunch 后找不到对应的 UActorChannel 通道处理,服务器不会响应这个 RPC 调用

UFUNCTION( Client )

用于声明由服务器发起调用,在客户端执行的 RPC 函数。
无论是否可靠,构造的 RPC Bunch 都会由 UChannel::SendBunch() 立即发送。

执行空间
  1. 在客户端调用时则仅本地执行
  2. 在服务器调用时,对于本地权威角色的 AActor ,若其参与属性同步且有所属链接则远程执行,否则仅本地执行
  3. 在服务器调用时,对于非本地权威角色的 AActor ,若其参与属性同步且有所属链接则远程执行,否则终止执行
远程执行的过滤情况
  1. 若 AActor 在 PendingKill 或 Unreachable 状态,或已对其调用 UWorld::DestroyActor() 来请求析构,则服务器会抛弃调用
  2. 若是不可靠的 RPC 调用,且链接发送缓存已饱和,则服务器会抛弃此调用
  3. 若还未在链接中为 AActor 创建通道,则要验证链接的客户端已加载此 AActor 所在的 ULevel 后,才为 AActor 创建通道,否则抛弃调用
  4. 若通道还未发送 OpenBunch ,则会自动发送一个 AActor 属性同步的 Bunch 作为 OpenBunch 促使客户端创建 AActor 和通道 ,然后才继续进行 RPC 调用

UFUNCTION( NetMulticast )

用于声明由服务器发起调用,并广播到所有客户端执行的 RPC 函数。
若为可靠的则 RPC Bunch 会立即发送,若为不可靠的则 RPC Bunch 会随着下次此 RPC 函数所在对象向此链接进行属性同步时,才会一起发送。
此外注意,由于在服务器的 UNetDriver 可能会使用 UReplicationGraph 来处理同步,因此这里的过滤情况分为两种。

执行空间
  1. 在客户端调用时仅本地执行
  2. 在服务器调用时,对于参与同步的 AActor 即会本地执行也会远程执行,对于不参与同步的 AActor 仅本地执行
UNetDriver 处理时远程执行的过滤情况
  1. 遍历 UNetDriver->ClientConnections 数组中的每个链接,进行如下检测来分发
  2. 调用 AActor::IsNetRelevantFor() 来检测此 AActor 和每个链接是否相关,若不相关则并不向该链接分发。注意,若调用的是可靠的广播 RPC 函数,当 AActor 和链接不相关,但还没有从此链接中移除此 AActor 通道前,也任向此链接分发
  3. 若还未在链接中为 AActor 创建通道,则需要验证链接的客户端已加载此 AActor 所在的 ULevel 后,才为 AActor 创建通道,否则抛弃调用
  4. 若通道还未发送 OpenBunch ,则会自动发送一个 AActor 属性同步的 Bunch 作为 OpenBunch 促使客户端创建 AActor 和通道 ,然后才继续进行 RPC 调用
UReplicationGraph 处理时远程执行的过滤情况
  1. 若 AActor 在 PendingKill 或 Unreachable 状态,或已对其调用 UWorld::DestroyActor() 来请求析构,则服务器会抛弃调用,不对任何链接进行分发
  2. 否则,遍历 UReplicationGraph->Connections 数组中的每个链接,进行如下检测来分发
  3. 若链接的 UNetConnection->ViewTarget 没有引用链接的观察对象,这表示链接还未创建好 APlayerController ,此时不向此链接分发
  4. 若当前链接的客户端还未已加载好此 AActor 所在的 ULevel,则不向此链接分发
  5. 若 AActor 在当前链接中还未打开 UActorChannel 通道,取决于是否为此 AActor 是否配置了同步范围。若未配置,则直接在此链接中为此 AActor 创建 UActorChannel 通道,并向此链接分发。 若配置了,则要验证链接的观察位置是否在此 AActor 同步范围内,才在链接中为 AActor 创建通道并继续分发,否则不向此链接分发。
  6. 若通道还未发送 OpenBunch ,则会自动发送一个 AActor 属性同步的 Bunch 作为 OpenBunch 促使客户端创建 AActor 和通道 ,然后才继续进行 RPC 调用
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值