IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 网络协议 -> 【RRQMSocket.RPC】C# 创建基于TCP协议的多序列化RPC -> 正文阅读

[网络协议]【RRQMSocket.RPC】C# 创建基于TCP协议的多序列化RPC


一、序言

本节须知

在学习本节之前,您必须熟悉RRQM中的Protocol服务器与客户端(或其派生类,例如文件传输和RPC)的创建,如果您不熟悉,请在下列链接中了解。


二、程序集源码、Demo下载

2.1 源码位置

RRQMSocket

2.2 Demo位置

RRQMBox

三、安装

安装RRQMSocket.RPC即可,具体步骤详看链接博客。

VS、Unity安装和使用Nuget包

四、创建RPC

在之前【RRQMSocket.RPC】C# 创建多协议、多方式、多语言、多平台RPC框架中,已经提到创建服务方法,但是由于没有解析器,所以无法对服务进行下一步设置,那么这节就让我们来详细创建服务吧。

4.1 创建服务

服务器项目中新建一个类,继承于ServerProvider类(或实现IServerProvider),然后在该类中写公共方法,并用RRQMRPC属性标签标记,如果方法有重载,需要重新指定函数键,方法可定义同步执行,也可以定义异步执行

public class MyRpcServer : ServerProvider
{
    [RRQMRPC]
    public string TestOne(int id)//同步服务
    {
        return $"若汝棋茗,id={id}";
    }

    [RRQMRPC("TestOne_Name")]//在重载服务时需要重新设定服务唯一键
    public string TestOne(int id, string name)
    {
        return $"若汝棋茗,Name={name},id={id}";
    }

    [RRQMRPC]
    public Task<string> AsyncTestOne(int id)//异步服务,尽量不要用Async结尾,不然生成的异步代码方法将出现两个Async
    {
        return Task.Run(() =>
        {
            return $"若汝棋茗,id={id}";
        });
    }
}

然后,创建TcpRpcParser解析器。在解析器中,设置类似TcpService

private static IRPCParser CreateRRQMTcpParser(int port)
{
    TcpRpcParser tcpRPCParser = new TcpRpcParser();

    //声明配置
    var config = new TcpRpcParserConfig();

    //继承TcpService配置
    config.ListenIPHosts = new IPHost[] {new IPHost(port) };//同时监听两个地址
    config.BufferLength = 1024 * 64;//缓存池容量
    config.BytePoolMaxSize = 512 * 1024 * 1024;//单个线程内存池容量
    config.BytePoolMaxBlockSize = 20 * 1024 * 1024;//单个线程内存块限制
    config.Logger = new Log();//日志记录器,可以自行实现ILog接口。
    config.ServerName = "RRQMService";//服务名称
    config.SeparateThreadReceive = false;//独立线程接收,当为true时可能会发生内存池暴涨的情况
    config.ThreadCount = 5;//多线程数量,当SeparateThreadReceive为false时,该值只决定BytePool的数量。
    config.Backlog = 30;
    config.ClearInterval = 60 * 1000;//60秒无数据交互会清理客户端
    config.ClearType = ClearType.Receive | ClearType.Send;//清理统计
    config.MaxCount = 10000;//最大连接数

    //继承TokenService配置
    config.VerifyToken = "123RPC";//连接验证令箭,可实现多租户模式
    config.VerifyTimeout = 3 * 1000;//验证3秒超时

    //继承ProcotolService配置
    config.CanResetID = true;//是否重新设置ID

    //继承TcpRpcParser配置,以实现RPC交互
    config.NameSpace = "RRQMTest";
    config.RPCVersion = new Version(1, 0, 0, 0);//定义此次发布的RPC版本。
    config.ProxyToken = "RPC";//代理令箭,当客户端获取代理文件,或服务时需验证令箭

    //载入配置
    tcpRPCParser.Setup(config);

    //启动服务
    tcpRPCParser.Start();

    Console.WriteLine($"TCP解析器添加完成,端口号:{port},VerifyToken={tcpRPCParser.VerifyToken},ProxyToken={tcpRPCParser.ProxyToken}");
    return tcpRPCParser;
}

最后添加解析器(添加时需要以键、值方式添加,方便后续查找),然后注册服务即可。

服务器创建完成。

static void Main(string[] args)
{
    //实例化RPCService
    RPCService rpcService = new RPCService();

    //添加解析器,解析器根据传输协议,序列化方式的不同,调用RPC服务
    rpcService.AddRPCParser("tcpRpcParser", CreateRRQMTcpParser(7794));
    
    //注册服务
    rpcService.RegisterServer<MyRpcServer>();

    //注册当前程序集的所有服务
    //rpcService.RegisterAllServer();
    Console.WriteLine("RPC服务已启动");
}

五、发现、调用RPC服务

实例化TcpRpcClient,然后依托于内部方法,完成服务的发现与调用。

static void Main(string[] args)
{
    TcpRpcClient client = new TcpRpcClient();

    var config = new TcpRpcClientConfig();
    config.RemoteIPHost = new IPHost("127.0.0.1:7794");
    config.VerifyToken = "123RPC";
    config.ProxyToken = "RPC";

    client.Setup(config);

    try
    {
        //1.先连接
        client.Connect();
        Console.WriteLine("连接成功");

        //2.然后发现服务
        MethodItem[] methodItems = client.DiscoveryService();
        Console.WriteLine("服务发现成功");

        foreach (var item in methodItems)
        {
            Console.WriteLine($"服务{item.ServerName}中的‘{item.Method}’可以调用");
        }

        Console.WriteLine("按任意键调用TestOne");
        Console.ReadKey();

        //3.调用
        string returnString = client.Invoke<string>("TestOne", InvokeOption.WaitInvoke, 10);

        Console.WriteLine($"调用成功,结果={returnString}");
        Console.ReadKey();
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message);
    }
}

六、调用痛点

RPC的创建与调用其实是一个比较简单的过程,但是要想更加智能化便捷化的使用,还有很长的一段路要走。

在上述示例中不难发现,RPC的调用实际上是一个非常麻烦难以维护的过程。麻烦的是调用者必须准确的知晓服务方法名称,且要传入类型结构相同的数据类型,尤其是后者,麻烦点甚多。而维护,也是疲不胜烦,每当服务端增加、修改、删除某个服务时,客户端也必须同步修改,这给产品更新,增加了不小的难度。

所以,我们想要的RPC绝不能仅仅是这样的…破玩意。

七 、获取代理

使用RPC的原则就是像使用本地方法一样,让开发者感觉不到任何的不同。所以就必须把服务代理到本地,常见的方式有三种,动态代理静态织入静态编译。三种方式殊途同归,最终都是构建本地数据结构,然后和远程通信。三种方式各有优缺,具体如下:

优缺点动态代理静态织入静态编译
优点动态构建类,灵活、适应性强。静态代码生成,自定义类参数自动生成,修改较灵活,调用效率高自定义类参数自动生成,密封性强,安全性高,调用效率高。
缺点调用效率较低,自定义类参数须自行构建,实现须IL支持,对调用平台有要求,例如:IOS不允许动态类生成,则不可使用。项目代码管理难统一,强迫症猝死服务一旦有破坏性升级,则必须重新替换dll,灵活性几乎为0。

说明:

在RRQM中支持静态织入静态编译两种方式,同时也预留了动态代理接口,但是由于作者精力有限,对IL较陌生,所以暂未实现动态代理,所以有IL强者有兴趣的话,请联系作者QQ:505554090,我们共同学习。

7.1 从服务器直接获取代理代码

实际上在TcpRpcParser完成服务注册以后,获取其Codes属性,即可获得代理代码。

此时,你可以复制、或者直接把代理代码写成源代码(cs文件)。

然后你可以把这个代码引入到客户端

CellCode[] cellCodes = tcpRpcParser.Codes;
foreach (var item in cellCodes)
{
    Console.WriteLine(item.Code);

    File.WriteAllText(item.Name+".cs",item.Code);
}

在这里插入图片描述

7.2 编译代理代码

有时候,为防止篡改生成的代码,不想把代理代码直接投入使用,那可以考虑将代码单独编译成dll,在Net45及以上平台,支持直接编译。

然后将编译代码加载到客户端。

tcpRpcParser.CompilerProxy();

在这里插入图片描述

7.3 远程通过静态织入获取代理载体

需要依靠插件(或程序),直接获取服务,然后将代理源文件植入项目中。具体步骤如下:

a).下载、安装RRQMRPCVSIX插件

【下载】
访问以下任意连接下载插件,然后安装“RRQMRPCVSIX.vsix”即可。

b).下载、运行RRQMRPCTool工具

如果完成了a步骤,则可以忽略本步骤。如果VS版本太低,或不想安装VS插件,那下载 RRQMRPCTool运行工具即可。

解压后直接运行exe程序,界面和c步骤一致。

在这里插入图片描述

c).获取代理

【插件使用】
1.安装(下载)完成后,右击项目,即可见“重新引用RPC”菜单条目。
2.点击,填入参数,确定即可。
3.完成后,项目中会多出“RRQMRPC”文件夹,里面的文件既是代理文件。

【RRQMRPCTool使用】
1.运行exe后,需要先选择“目标项目”,选择到客户端项目的.csproj文件。
2.然后,填入参数,确定即可。
3.完成后,项目中会多出“RRQMRPC”文件夹,里面的文件既是代理文件。

图片名称 图片名称 图片名称

八、使用

在引入代理代码(dll或者.cs源码)后,即可以使用代理的类(MyRpcServer)。

使用代理的优点如下:

  • 维护简单,客户端只需要重新运行工具即可获得新代理。
  • 数据结构生成,假如RPC方法的参数是复杂类型,则会直接生成相同结构的数据。
  • 方法说明,客户独很好的得知该方法的注释。

基础操作如下:

static void Main(string[] args)
{
    TcpRpcClient client = new TcpRpcClient();

    var config = new TcpRpcClientConfig();
    config.RemoteIPHost = new IPHost("127.0.0.1:7794");
    config.VerifyToken = "123RPC";
    config.ProxyToken = "RPC";

    client.Setup(config);

    try
    {
        //1.先连接
        client.Connect();
        Console.WriteLine("连接成功");

        //2.然后发现服务
        MethodItem[] methodItems = client.DiscoveryService();
        Console.WriteLine("服务发现成功");

        foreach (var item in methodItems)
        {
            Console.WriteLine($"服务{item.ServerName}中的‘{item.Method}’可以调用");
        }

        Console.WriteLine("按任意键调用TestOne");
        Console.ReadKey();

        //3.实例化服务代理,传入IRpcClient

        MyRpcServer myRpcServer = new MyRpcServer(client);

        //4.通过MyRpcServer代理类直接调用
        string returnString = myRpcServer.TestOne(10);

        Console.WriteLine($"调用成功,结果={returnString}");
        Console.ReadKey();
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message);
    }
}

  网络协议 最新文章
使用Easyswoole 搭建简单的Websoket服务
常见的数据通信方式有哪些?
Openssl 1024bit RSA算法---公私钥获取和处
HTTPS协议的密钥交换流程
《小白WEB安全入门》03. 漏洞篇
HttpRunner4.x 安装与使用
2021-07-04
手写RPC学习笔记
K8S高可用版本部署
mySQL计算IP地址范围
上一篇文章      下一篇文章      查看所有文章
加:2021-11-25 08:28:41  更:2021-11-25 08:30:13 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 7:30:19-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码