仿照这位Up主写的:Up主视频
项目地址:在这
首先 什么事RPC
- 看知乎上这几个回答
就像在本地调用函数一样去调用远程的函数
,但是本地和远端拥有不同内存空间直接调用肯定是没有办法的,所以思路就是,我在本地调用方法,内部实现利用网络消息传输,去调用远程的函数。本质还是消息协议,只不过是在解析完协议又封装了一层用于调用具体的方法
注意
- up主用的的是
大端(高尾端)
,我这个因为习惯用的是小端(低尾端)
- 剩下的就是写法问题,没什么太大差别
- 只是为了了解具体的思路,里面存在很多问题,比如没有处理粘包分包问题,字节缓存,运行时直接利用反射等等。
客户端(调用端)
- 首先我们上面说的
就像在本地调用函数一样去调用远程的函数
。仔细想想,A调用函数Func实际执行的是B的Func。好了,现在有了一个规范了两端都要实现
同样的函数。看红字强调了实现这2个字,所以我们采用接口形式,两端都实现接口IUserInfo
需要实现是登陆与加法
interface IUserInfo
{
bool Login(string account, string pwd);
int Add(int a, int b);
}
- 在看具体实现先看一下使用的几个数据结构,内置对象的枚举,ArgTypeInfo是参数附带类型枚举。
public class ArgTypeInfo
{
public TypeEnum argType {
get; set; }
public object value {
get; set; }
}
public enum TypeEnum
{
Void = 0,
Int,
Bool,
String
}
- EncodeSendPackage主要用于将接口名称、方法名称、参数个数、返回类型以及参数,然后依照下面的RPC协议格式进行编码得到字节数组。
private static byte[] EncodeSendPackage(string interfaceName, string methodName, int argLen, TypeEnum returnType, List<ArgTypeInfo> argTypeInfos
)
{
List<byte> byteList = new List<byte>();
byte[] interfaceBytes = Encoding.UTF8.GetBytes(interfaceName);
byteList.Add((byte)interfaceBytes.Length);
byteList.AddRange(interfaceBytes);
byte[] methodNameBytes = Encoding.UTF8.GetBytes(methodName);
byteList.Add((byte)methodNameBytes.Length);
byteList.AddRange(methodNameBytes);
byteList.Add((byte)argLen);
byteList.Add((byte)returnType);
foreach (ArgTypeInfo ati in argTypeInfos)
{
byteList.Add((byte)ati.argType);
if (ati.argType == TypeEnum.String)
{
string value = ati.value as string;
byte[] stringBytes = Encoding.UTF8.GetBytes(value);
byteList.Add((byte)stringBytes.Length);
byteList.AddRange(stringBytes);
}
else if (ati.argType == TypeEnum.Int)
{
int value = Convert.ToInt32(ati.value);
byte[] intBytes = Int2Bytes(value);
byteList.AddRange(intBytes);
}
else if (ati.argType == TypeEnum.Bool)
{