前言
Ue4在网络方面主要通过两种方式来同步,一种是属性复制,另一种就是RPC。这里官网也有解释。
一、RPC架构组件
基本的RPC架构应该包含至少四个组件: <1>客户端(Client):服务调用方(人)
<2>客户端存根(Client Stub):存放服务端地址信息,将客户端地址信息,将客户端的请求参数数据信息打包成网络消息,再通过网络传输发送给服务端
<3>服务端存根(Server-Stub):接收客户端发送过来的请求消息并进行解包,然后在调用本地服务进行处理
<4>服务端(Server):服务真正的提供者
二、RPC调用过程
<1>服务消费者(客户端):通过本地调用的方式调用服务
<2>客户端存根(Client Stub):接收到调用请求后负责将方法,入参等信息序列化(组装)成能够进行网络传输的消息体
<3>客户端存根(Client Stub):找到远程的服务地址,并且将消息通过网络发送给服务端。
<4>服务端存根(Server Stub):收到消息后进行解码(反序列化操作)
<5>服务端存根(Server Stub):根据解码结果调用本地的服务进行相关处理
<6>本地服务执行具体业务逻辑并将处理结果返回给服务端存根(Server Stub)
<7>服务端存根(Server Stub):将返回的结果重新打包成消息(序列化)并通过网络发送至消费方
<8>客户端存根(Client Stub):接收到消息,并进行解码(反序列化)
<9>服务消费方得到最终结果
注: 1.序列化: 当A机器上的应用发起一个RPC调用时,调用方法和其入参等信息需要通过底层的网络协议如TCP传输到B机器,由于网络协议是基于二进制的,所有我们传输的参数数据都需要先进行序列化(Serialize)或者编组(marshal)成二进制的形式才能在网络中进行传输。然后通过寻址操作和网络传输将序列化或者编组之后的二进制数据发送给B机器。
2.反序列化: 当B机器接收到A机器的应用发来的请求之后,又需要对接收到的参数等信息进行反序列化操作(序列化的逆操作),即将二进制信息恢复为内存中的表达方式,然后再找到对应的方法(寻址的一部分)进行本地调用(一般是通过生成代理Proxy去调用,通常会有JDK动态代理、CGLIB动态代理、Javassist生成字节码技术等),之后得到调用的返回值。
3.服务调用: B机器进行本地调用(通过代理Proxy)之后得到了返回值,此时还需要再把返回值发送回A机器,同样也需要经过序列化操作,然后再经过网络传输将二进制数据发送回A机器,而当A机器接收到这些返回值之后,则再次进行反序列化操作,恢复为内存中的表达方式,最后再交给A机器上的应用进行相关处理(一般是业务逻辑处理操作)。
4.服务寻址: 可靠的寻址方式(主要是提供服务的发现)是RPC的实现基石,比如可以采用redis或者zookeeper来注册服务
三、C++调用RPC
要将一个函数声明为RPC,只需将Server,Client或NetMulticast关键字添加到UFUNCTION声明。 <1>将某个函数声明为一个要在服务器上调用,需在客户端执行的RPC,可以这样:
UFUNCTION( Client );
void ClientRPCFunction();
<2>要将某个函数在一个客户端上调用,但需在服务器上执行的RPC,可以采用类似方法。但需使用Server关键字:
UFUNCTION( Server );
void ServerRPCFunction();
<3>还有一种叫多播(Multicast)的特殊RPC函数。多播RPC可以从服务器调用,然后在服务器和当前连接的所有客户端上执行。如果声明多播函数,只需要NetMulticast关键字:
UFUNCTION( NetMulticast );
void MulticastRPCFunction();
多播RPC还可以从客户端调用,但这时只能在本地执行。
四、C++调用RPC要求
<1>它们必须从Actor调用
<2>Actor必须被复制
<3>如果RPC是从服务器调用并在客户端上执行,则只有实际拥有这个Actor的客户端才会执行这个函数
<4>如果RPC是从客户端调用并在服务器上执行,客户端就必须拥有调用RPC的Actor
<5>多播RPC是个例外: 1.如果它们是从服务器调用,服务器将在本地和所有已连接的客户端上执行它们
2.如果它们是从客户端调用,则只在本地而非服务器上执行
3.在特定的Actor的网络更新期内,多播函数将不会复制两次以上。
1.从服务器调用的RPC
2.从客户端调用的RPC
总结
以上就是我搜寻的知识总结,全是理论知识,没什么技术含量,加上自己的一些感悟,准备深入UE网络,把这个当做一个方向,有一起研究的可以+q:3497995954
|