mirror of
https://github.com/RRQM/TouchSocket.git
synced 2025-12-18 17:36:43 +08:00
436 lines
12 KiB
Plaintext
436 lines
12 KiB
Plaintext
---
|
||
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"/>
|