Files
TouchSocket/handbook/docs/jsonrpc.mdx
2024-11-12 21:39:50 +08:00

436 lines
12 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
id: jsonrpc
title: 产品及架构介绍
---
import Tag from "@site/src/components/Tag.js";
import CardLink from "@site/src/components/CardLink.js";
### 定义
命名空间TouchSocket.JsonRpc <br/>
程序集:[TouchSocket.JsonRpc.dll](https://www.nuget.org/packages/TouchSocket.JsonRpc)
## 一、说明
JsonRpc是**通用**的Rpc规范与**编程语言无关、操作系统无关**。详细说明请参阅[JsonRpc 2.0 官方文档](https://www.jsonrpc.org/specification)在TouchSocket中封装了**前后端**,使其使用更加方便、高效。
目前支持`Tcp`、`Http`、`WebSocket`三种协议调用。
## 二、特点:
- **异常反馈** 。
- 插件支持。
- 支持自定义类型。
- 支持类型嵌套。
- 支持js、Android等调用。
- 支持服务器主动调用客户端
## 三、定义服务
在**服务器**端中新建一个类,继承于`RpcServer`类(或实现`IRpcServer`),然后在该类中写**公共方法**,并用**JsonRpc**特性标签标记。
```csharp showLineNumbers
public partial class JsonRpcServer : RpcServer
{
/// <summary>
/// 使用调用上下文。
/// 可以从上下文获取调用的SessionClient。从而获得IP和Port等相关信息。
/// </summary>
/// <param name="callContext"></param>
/// <param name="str"></param>
/// <returns></returns>
[JsonRpc(MethodInvoke =true)]
public string TestGetContext(ICallContext callContext, string str)
{
if (callContext.Caller is IHttpSessionClient SessionClient)
{
if (SessionClient.Protocol == Protocol.WebSocket)
{
Console.WriteLine("WebSocket请求");
var client = callContext.Caller as IHttpSessionClient;
var ip = client.IP;
var port = client.Port;
Console.WriteLine($"WebSocket请求{ip}:{port}");
}
else
{
Console.WriteLine("HTTP请求");
var client = callContext.Caller as IHttpSessionClient;
var ip = client.IP;
var port = client.Port;
Console.WriteLine($"HTTP请求{ip}:{port}");
}
}
else if (callContext.Caller is ITcpSessionClient)
{
Console.WriteLine("Tcp请求");
var client = callContext.Caller as ITcpSessionClient;
var ip = client.IP;
var port = client.Port;
Console.WriteLine($"Tcp请求{ip}:{port}");
}
return "RRQM" + str;
}
[JsonRpc(MethodInvoke = true)]
public JObject TestJObject(JObject obj)
{
return obj;
}
[JsonRpc(MethodInvoke = true)]
public string TestJsonRpc(string str)
{
return "RRQM" + str;
}
}
```
:::info 备注
设置`MethodInvoke = true`即以方法名作为调用键这也是JsonRpc规范所规定。但同时框架内部还支持另一种方式即默认情况下会使用方法的**全名称小写**作为调用键(即:命名空间.类名.方法名)
:::
## 四、启动服务器
JsonRpc支持多个基本协议的服务器所以下面将一一介绍。
更多注册Rpc的方法请看[注册Rpc服务](./rpcregister.mdx)
### 4.1 以Tcp为基础协议
当以Tcp为基础协议时支持Tcp的任何操作。包括但不限于`设置适配器`等。
下列代码创建的就是一个最普通Tcp协议下的JsonRpc服务器。该服务支持任何未处理的Tcp协议的JsonRpc数据包调用。
```csharp showLineNumbers
var service = new TcpService();
await service.SetupAsync(new TouchSocketConfig()
.SetListenIPHosts(7705)
.ConfigureContainer(a =>
{
a.AddRpcStore(store =>
{
store.RegisterServer<JsonRpcServer>();
});
})
.ConfigurePlugins(a =>
{
/*
使用tcp服务器的时候默认情况下会把所有连接的协议都转换为JsonRpcUtility.TcpJsonRpc。
这样所有的数据都会被尝试解释为JsonRpc。
如果不需要该功能可以调用NoSwitchProtocol()。
*/
a.UseTcpJsonRpc();
}));
await service.StartAsync();
```
:::caution 注意
因为上述服务器中没有使用任何适配器,所以在实际使用中,可能会发生数据包粘包、分包等问题。所以不建议直接使用。要想投入生产使用,最简单也建议使用`.SetTcpDataHandlingAdapter(() => new TerminatorPackageAdapter("\r\n"))`换行符分割等适配器。
:::
### 4.2 使用Http协议服务器
创建后如果想使用Http调用只需要以Post方式将调用Json字符串路由到设定路由地址即可下文示例“/jsonRpc”
```csharp showLineNumbers
var service = new HttpService();
await service.SetupAsync(new TouchSocketConfig()
.SetListenIPHosts(7706)
.ConfigureContainer(a =>
{
a.AddRpcStore(store =>
{
store.RegisterServer<JsonRpcServer>();
});
})
.ConfigurePlugins(a =>
{
a.UseHttpJsonRpc()
.SetJsonRpcUrl("/jsonRpc");
}));
await service.StartAsync();
```
### 4.3 使用WebSocket协议服务器
如果想使用Websocket调用只需要以**文本**形式,传递到服务器即可。
```csharp showLineNumbers
var service = new HttpService();
await service.SetupAsync(new TouchSocketConfig()
.SetListenIPHosts(7707)
.ConfigureContainer(a =>
{
a.AddRpcStore(store =>
{
store.RegisterServer<JsonRpcServer>();
});
})
.ConfigurePlugins(a =>
{
a.UseWebSocket()
.SetWSUrl("/ws");
a.UseWebSocketJsonRpc()
.SetAllowJsonRpc((SessionClient, context) =>
{
//此处的作用是通过连接的一些信息判断该ws是否执行JsonRpc。
//当然除了此处可以设置外也可以通过SessionClient.SetJsonRpc(true)直接设置。
return true;
});
}));
await service.StartAsync();
```
:::tip 提示
`WebSocket`协议服务器和`Http`协议服务器可以合并为一个。
:::
## 五、通用调用
因为`JsonRpc`是通用调用协议,所以只要**适配基础协议**,即可直接使用`Json`字符串调用。
以下字符串只是示例具体的method参数应当遵循当前路由。
### 5.1 Tcp协议直接调用
在`Tcp`协议时,按照适配器,选择性的是否以`\r\n`结尾。
```csharp showLineNumbers
{"jsonrpc": "2.0", "method": "testjsonrpc", "params":["RRQM"], "id": 1}
```
### 5.2 Http协议直接调用
在`Http`协议时,以`Url+Post`方式即可
```csharp showLineNumbers
{"jsonrpc": "2.0", "method": "testjsonrpc", "params":["RRQM"], "id": 1}
```
### 5.3 Websocket协议直接调用
在`Websocket`协议时,以`文本类型`,直接发送到服务器即可。
```csharp showLineNumbers
{"jsonrpc": "2.0", "method": "testjsonrpc", "params":["RRQM"], "id": 1}
```
## 六、客户端直接调用
框架内部提供了`JsonRpc`的专属客户端,可以直接调用,也可以生成代理调用。下列将详细介绍。
### 6.1 Tcp协议
```csharp showLineNumbers
var client = new TcpJsonRpcClient();
await client.SetupAsync(new TouchSocketConfig()
.SetRemoteIPHost("127.0.0.1:7705"));
await client.ConnectAsync();
string result = client.InvokeT<string>("TestJsonRpc", InvokeOption.WaitInvoke, "RRQM");
```
### 6.2 Http协议
```csharp showLineNumbers
var client = new HttpJsonRpcClient();
await client.SetupAsync(new TouchSocketConfig()
.SetRemoteIPHost("http://127.0.0.1:7706/jsonrpc"));
await client.ConnectAsync();
string result = client.InvokeT<string>("TestJsonRpc", InvokeOption.WaitInvoke, "RRQM");
```
### 6.3 Websocket协议
```csharp showLineNumbers
var client = new WebSocketJsonRpcClient();
await client.SetupAsync(new TouchSocketConfig()
.SetRemoteIPHost("ws://127.0.0.1:7707/ws"));//此url就是能连接到websocket的路径。
await client.ConnectAsync();
string result = client.InvokeT<string>("TestJsonRpc", InvokeOption.WaitInvoke, "RRQM");
```
### 6.4 生成代理调用
在服务器端,注册完服务后,就可以生成客户端调用代码了。详细的操作可以查看[服务端代理生成](./rpcgenerateproxy.mdx)
```csharp {8-9}
a.AddRpcStore(store =>
{
store.RegisterServer<JsonRpcServer>();
#if DEBUG
//下列代码,会生成客户端的调用代码。
var codeString = store.GetProxyCodes("JsonRpcServerProxy", typeof(JsonRpcAttribute));
File.WriteAllText("../../../JsonRpcServerProxy.cs", codeString);
#endif
});
```
然后把生成的`.cs`文件复制(或链接)到客户端项目。然后客户端直接使用同名`扩展方法`即可调用。
```csharp showLineNumbers
var sum3 = client.TestJsonRpc("RRQM");
```
### 6.5 使用DispatchProxy代理调用
使用`DispatchProxy`代理调用,可以实现动态代理,详情请看[DispatchProxy代理生成](./rpcgenerateproxy.mdx)
首先,需要声明一个基类,用于通讯基础。
```csharp showLineNumbers
/// <summary>
/// 新建一个类继承JsonRpcDispatchProxy亦或者RpcDispatchProxy基类。
/// 然后实现抽象方法主要是能获取到调用的IRpcClient派生接口。
/// </summary>
internal class MyJsonRpcDispatchProxy : JsonRpcDispatchProxy
{
private readonly IJsonRpcClient m_client;
public MyJsonRpcDispatchProxy()
{
this.m_client = CreateJsonRpcClientByTcp().GetFalseAwaitResult();
}
public override IJsonRpcClient GetClient()
{
return this.m_client;
}
private static async Task<IJsonRpcClient> CreateJsonRpcClientByTcp()
{
var client = new TcpJsonRpcClient();
await client.SetupAsync(new TouchSocketConfig()
.SetRemoteIPHost("127.0.0.1:7705")
.SetTcpDataHandlingAdapter(() => new TerminatorPackageAdapter("\r\n")));
await client.ConnectAsync();
return client;
}
}
```
:::tip 提示
此处其他协议的`JsonRpc`也是完全支持的。
:::
然后按照服务,定义一个相同的代理接口。
```csharp showLineNumbers
interface IJsonRpcServer
{
[JsonRpc(MethodInvoke = true)]
string TestJsonRpc(string str);
}
```
最后生成代理,并按照接口调用。
```csharp {1}
var rpc = MyJsonRpcDispatchProxy.Create<IJsonRpcServer, MyJsonRpcDispatchProxy>();
while (true)
{
var result = rpc.TestJsonRpc(Console.ReadLine());
Console.WriteLine(result);
}
```
## 七、反向Rpc服务器主动调用客户端
框架提供了反向`Rpc`,即**服务器主动调用客户端**。该功可以用于`Web`等多端。
反向Rpc必须在全双工协议下使用如`WebSocket`、`Tcp`等。
具体使用如下:
首先,需要在*客户端*像常规`Rpc`一样声明一个`Rpc`服务。然后需要使用`JsonRpc`特性表示。
```csharp {3} showLineNumbers
public partial class ReverseJsonRpcServer : RpcServer
{
[JsonRpc(MethodInvoke = true)]
public int Add(int a, int b)
{
return a + b;
}
}
```
然后注册服务。
```csharp {5-8} showLineNumbers
var jsonRpcClient = new WebSocketJsonRpcClient();
await jsonRpcClient.SetupAsync(new TouchSocketConfig()
.ConfigureContainer(a =>
{
a.AddRpcStore(store =>
{
store.RegisterServer<ReverseJsonRpcServer>();
});
})
.SetRemoteIPHost("ws://127.0.0.1:7707/ws"));//此url就是能连接到websocket的路径。
await jsonRpcClient.ConnectAsync();
```
在服务器端中,拿到`IHttpSessionClient`对象。然后调用`GetJsonRpcActionClient`扩展方法获取到`IJsonRpcClient`。然后调用`Invoke`等。
下列示例演示的是,当`WebSocket`连接上时,服务器主动调用客户端。
```csharp {8,10} showLineNumbers
class MyPluginClass : PluginBase, IWebSocketHandshakedPlugin
{
public async Task OnWebSocketHandshaked(IHttpSessionClient client, HttpContextEventArgs e)
{
try
{
//获取JsonRpcActionClient用于执行反向Rpc
var jsonRpcClient = client.GetJsonRpcActionClient();
var result = await jsonRpcClient.InvokeTAsync<int>("Add", InvokeOption.WaitInvoke, 10, 20);
Console.WriteLine($"反向调用成功,结果={result}");
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
await e.InvokeNext();
}
}
```
:::tip
反向JsonRpc也能使用代理。
:::
## 八、本文示例Demo
<CardLink link="https://gitee.com/RRQM_Home/TouchSocket/tree/master/examples/JsonRpc"/>