对于想了解jsonRPC的读者,本文将是一篇不可错过的文章,我们将详细介绍jsonRPC节点,并且为您提供关于.NET开源项目StreamJsonRpc介绍、.NET开源项目StreamJsonRpc
对于想了解jsonRPC的读者,本文将是一篇不可错过的文章,我们将详细介绍jsonRPC 节点,并且为您提供关于.NET 开源项目 StreamJsonRpc 介绍、.NET 开源项目 StreamJsonRpc 介绍 [上篇]、.NET 开源项目 StreamJsonRpc 介绍 [中篇]、.NET 开源项目 StreamJsonRpc 介绍[下篇]的有价值信息。
本文目录一览:- jsonRPC(jsonRPC 节点)
- .NET 开源项目 StreamJsonRpc 介绍
- .NET 开源项目 StreamJsonRpc 介绍 [上篇]
- .NET 开源项目 StreamJsonRpc 介绍 [中篇]
- .NET 开源项目 StreamJsonRpc 介绍[下篇]
jsonRPC(jsonRPC 节点)
<?php
/**
* Simple JSON-RPC interface.
*/
namespace org;
class JsonRpc
{
protected $host, $port, $version;
protected $id = 0;
function __construct($host, $port, $version="2.0")
{
$this->host = $host;
$this->port = $port;
$this->version = $version;
}
function request($method, $params=array())
{
$data = array();
$data[''jsonrpc''] = $this->version;
$data[''id''] = $this->id++;
$data[''method''] = $method;
$data[''params''] = $params;
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $this->host);
curl_setopt($ch, CURLOPT_PORT, $this->port);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
curl_setopt($ch, CURLOPT_HTTPHEADER, array(''Content-Type: application/json''));
curl_setopt($ch, CURLOPT_POST, TRUE);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
$ret = curl_exec($ch);
if($ret !== FALSE)
{
$formatted = $this->format_response($ret);
if(isset($formatted->error))
{
//throw new RPCException($formatted->error->message, $formatted->error->code);
return false;
}
else
{
return $formatted;
}
}
else
{
throw new \think\Exception("Server did not respond");
}
}
function format_response($response)
{
return @json_decode($response);
}
}
// class RPCException extends Exception
// {
// public function __construct($message, $code = 0, Exception $previous = null)
// {
// parent::__construct($message, $code, $previous);
// }
// public function __toString()
// {
// return __CLASS__ . ": ".(($this->code > 0)?"[{$this->code}]:":"")." {$this->message}\n";
// }
// }
.NET 开源项目 StreamJsonRpc 介绍
StreamJsonRpc 是一个实现了 JSON-RPC 通信协议的开源 .NET 库,在介绍 StreamJsonRpc 之前,我们先来了解一下 JSON-RPC。
JSON-RPC 介绍
JSON-RPC 是一个无状态且轻量级的远程过程调用(RPC)协议,其使用 JSON(RFC 4627)作为数据格式。
目前 JSON-RPC 的版本已发展到 2.0,JSON-RPC 2.0 与 1.0 的约定规范是不一样的。2.0 包含一个名为 jsonrpc
且值为 2.0
的成员,而 1.0 版本是不包含的。所以我们可以很容易在两个版本间区分出 2.0。
JSON-RPC 在客户端与服务端之间交换的所有成员名应是区分大小写的,函数、方法、过程都认为是可互换的。客户端被定义为请求对象的来源及响应对象的处理程序;服务端被定义为响应对象的起源和请求对象的处理程序。
请求对象
发送一个请求对象至服务端代表一个 RPC 调用,JSON-RPC 2.0 规定一个请求对象包含下列成员:
- jsonrpc:指定 JSON-RPC 协议版本的字符串,必须准确写为“2.0”。
- method:包含所要调用方法名称的字符串,以 rpc 开头的方法名,用英文句号连接的为预留给 rpc 内部的方法名及扩展名,且不能在其他地方使用。
- params:调用方法所需要的结构化参数值,该成员参数可以被省略。
- id:已建立客户端的唯一标识,值必须包含一个字符串、数值或 NULL 空值。如果不包含该成员则被认定为是一个通知。该值一般不为 NULL,若为数值则不应该包含小数。
没有包含 id
成员的请求对象为通知,作为通知的请求对象表明客户端对服务端响应不感兴趣,服务端可以不响应请求对象给客户端。
下面是几个请求对象的 JSON 结构示例(“-->”表示发送,“<--”表示响应,下同):
--> { "jsonrpc": "2.0", "method": "subtract", "params": [42, 23], "id": 1 }
--> { "jsonrpc": "2.0", "method": "subtract", "params": {"minuend": 42, "subtrahend": 23}, "id": 4}
--> {"jsonrpc": "2.0", "method": "update", "params": [1,2,3,4,5]} // 通知
响应对象
当客户端发起一个 RPC 调用时,除通知之外,服务端都必须回复响应。响应也表示为一个 JSON 对象,使用以下成员:
- jsonrpc:指定 JSON-RPC 协议版本的字符串,必须准确写为“2.0”。
- result:调用成功时响应给客户端的结果,当调用发生错误时可以不包含该成员。
- error:调用发生错误时返回给客户端的错误信息,在调用失败时必须包含该成员。
- id:对应请求对象的“id”,其值必须与请求对象中的“id”值一致。
响应对象必须包含 result 或 error 成员之一。
响应对象的 error 成员的结构包含下列成员:
- code:使用数值表示该异常的错误类型,必须为整数。、
- message:对该错误的简单描述字符串,该描述应尽量限定在简短的一句话。
- data:包含关于错误的附加信息,可忽略。
其中 -32768 至 -32000 为保留的预定义错误代码,各保留错误代码的含义请查看文末参考链接[1]。
下面是几个响应对象的 JSON 结构示例:
<-- {"jsonrpc": "2.0", "result": 19, "id": 1}
<-- {"jsonrpc": "2.0", "error": {"code": -32601, "message": "Method not found"}, "id": "1"}
<-- {"jsonrpc": "2.0", "error": {"code": -32700, "message": "Parse error"}, "id": null} // 无效调用
批量调用
当需要同时发送多个请求对象时,客户端可以发送一个包含所有请求对象的数组。
当批量调用的所有请求对象处理完成时,服务端则需要返回一个包含相对应的响应对象数组。每个响应对象都应对应每个请求对象,除非是通知的请求对象。服务端可以并发的,可以以任意顺序和任意宽度并行处理这些批量调用。而客户端应该是基于各个响应对象中的 id 成员来匹配对应的请求对象。
若批量调用没有需要返回的响应对象,则服务端不需要返回任何结果。
下面是一个批量请求及响应的 JSON 结构示例:
--> [
{"jsonrpc": "2.0", "method": "sum", "params": [1,2,4], "id": "1"},
{"jsonrpc": "2.0", "method": "notify_hello", "params": [7]},
{"foo": "boo"},
{"jsonrpc": "2.0", "method": "foo.get", "params": {"name": "myself"}, "id": "5"},
{"jsonrpc": "2.0", "method": "get_data", "id": "9"}
]
<-- [
{"jsonrpc": "2.0", "result": 7, "id": "1"},
{"jsonrpc": "2.0", "error": {"code": -32600, "message": "Invalid Request"}, "id": null},
{"jsonrpc": "2.0", "error": {"code": -32601, "message": "Method not found"}, "id": "5"},
{"jsonrpc": "2.0", "result": ["hello", 5], "id": "9"}
]
当批量请求对象都是通知时,服务端不需要返回结果。
StreamJsonRpc 库介绍
StreamJsonRpc 是一个实现了 JSON-RPC 通信协议的 .NET 库,支持 .NET Core。它把 RPC 的调用封装为公开的 .NET API,可以很方便的进行 RPC 请求的发送和接收操作。StreamJsonRpc 是微软官方的一个开源库,目前 Star 数接近 300,貌似知道的人不多或者用的人不多。GitHub 地址:
github.com/microsoft/vs-streamjsonrpc
StreamJsonRpc 可以在 Stream、WebSocket 或 System.IO.Pipelines 管道上工作,独立于底层传输。除了包含 JSON-RPC 规范所需的特性外,它额外还有如下优点:
- 请求取消
- .NET 事件作为通知
- 动态客户端代理生成
- 支持紧凑的 MessagePack 二进制序列化
- 易于实现插件式架构的消息处理和格式化
使用 StreamJsonRpc 主要有四个基本步骤:建立 JSON-RPC 连接、发送 RPC 请求、接收 RPC 请求、断开连接。
这一篇主要介绍一些预备知识,下一篇将通过示例演示并详细介绍 StreamJsonRpc 的使用,敬请期待!
参考:
[1].jsonrpc.org/specification
[2].github.com/microsoft/vs-streamjsonrpc
.NET 开源项目 StreamJsonRpc 介绍 [上篇]
StreamJsonRpc 是一个实现了 JSON-RPC 通信协议的开源 .NET 库,在介绍 StreamJsonRpc 之前,我们先来了解一下 JSON-RPC。
JSON-RPC 介绍
JSON-RPC 是一个无状态且轻量级的远程过程调用(RPC)协议,其使用 JSON(RFC 4627)作为数据格式。
目前 JSON-RPC 的版本已发展到 2.0,JSON-RPC 2.0 与 1.0 的约定规范是不一样的。2.0 包含一个名为 jsonrpc
且值为 2.0
的成员,而 1.0 版本是不包含的。所以我们可以很容易在两个版本间区分出 2.0。
JSON-RPC 在客户端与服务端之间交换的所有成员名应是区分大小写的,函数、方法、过程都认为是可互换的。客户端被定义为请求对象的来源及响应对象的处理程序;服务端被定义为响应对象的起源和请求对象的处理程序。
请求对象
发送一个请求对象至服务端代表一个 RPC 调用,JSON-RPC 2.0 规定一个请求对象包含下列成员:
jsonrpc:指定 JSON-RPC 协议版本的字符串,必须准确写为 “2.0”。
method:包含所要调用方法名称的字符串,以 rpc 开头的方法名,用英文句号连接的为预留给 rpc 内部的方法名及扩展名,且不能在其他地方使用。
params:调用方法所需要的结构化参数值,该成员参数可以被省略。
id:已建立客户端的唯一标识,值必须包含一个字符串、数值或 NULL 空值。如果不包含该成员则被认定为是一个通知。该值一般不为 NULL,若为数值则不应该包含小数。
没有包含 id
成员的请求对象为通知,作为通知的请求对象表明客户端对服务端响应不感兴趣,服务端可以不响应请求对象给客户端。
下面是几个请求对象的 JSON 结构示例(“-->” 表示发送,“<--” 表示响应,下同):
--> { "jsonrpc": "2.0", "method": "subtract", "params": [42, 23], "id": 1 }
--> { "jsonrpc": "2.0", "method": "subtract", "params": {"minuend": 42, "subtrahend": 23}, "id": 4}
--> {"jsonrpc": "2.0", "method": "update", "params": [1,2,3,4,5]} // 通知
响应对象
当客户端发起一个 RPC 调用时,除通知之外,服务端都必须回复响应。响应也表示为一个 JSON 对象,使用以下成员:
jsonrpc:指定 JSON-RPC 协议版本的字符串,必须准确写为 “2.0”。
result:调用成功时响应给客户端的结果,当调用发生错误时可以不包含该成员。
error:调用发生错误时返回给客户端的错误信息,在调用失败时必须包含该成员。
id:对应请求对象的 “id”,其值必须与请求对象中的 “id” 值一致。
响应对象必须包含 result 或 error 成员之一。
响应对象的 error 成员的结构包含下列成员:
code:使用数值表示该异常的错误类型,必须为整数。、
message:对该错误的简单描述字符串,该描述应尽量限定在简短的一句话。
data:包含关于错误的附加信息,可忽略。
其中 -32768 至 -32000 为保留的预定义错误代码,各保留错误代码的含义请查看文末参考链接 [1]。
下面是几个响应对象的 JSON 结构示例:
<-- {"jsonrpc": "2.0", "result": 19, "id": 1}
<-- {"jsonrpc": "2.0", "error": {"code": -32601, "message": "Method not found"}, "id": "1"}
<-- {"jsonrpc": "2.0", "error": {"code": -32700, "message": "Parse error"}, "id": null} // 无效调用
批量调用
当需要同时发送多个请求对象时,客户端可以发送一个包含所有请求对象的数组。
当批量调用的所有请求对象处理完成时,服务端则需要返回一个包含相对应的响应对象数组。每个响应对象都应对应每个请求对象,除非是通知的请求对象。服务端可以并发的,可以以任意顺序和任意宽度并行处理这些批量调用。而客户端应该是基于各个响应对象中的 id 成员来匹配对应的请求对象。
若批量调用没有需要返回的响应对象,则服务端不需要返回任何结果。
下面是一个批量请求及响应的 JSON 结构示例:
--> [
{"jsonrpc": "2.0", "method": "sum", "params": [1,2,4], "id": "1"},
{"jsonrpc": "2.0", "method": "notify_hello", "params": [7]},
{"foo": "boo"},
{"jsonrpc": "2.0", "method": "foo.get", "params": {"name": "myself"}, "id": "5"},
{"jsonrpc": "2.0", "method": "get_data", "id": "9"}
]
<-- [
{"jsonrpc": "2.0", "result": 7, "id": "1"},
{"jsonrpc": "2.0", "error": {"code": -32600, "message": "Invalid Request"}, "id": null},
{"jsonrpc": "2.0", "error": {"code": -32601, "message": "Method not found"}, "id": "5"},
{"jsonrpc": "2.0", "result": ["hello", 5], "id": "9"}
]
当批量请求对象都是通知时,服务端不需要返回结果。
StreamJsonRpc 库介绍
StreamJsonRpc 是一个实现了 JSON-RPC 通信协议的 .NET 库,支持 .NET Core。它把 RPC 的调用封装为公开的 .NET API,可以很方便的进行 RPC 请求的发送和接收操作。StreamJsonRpc 是微软官方的一个开源库,目前 Star 数接近 300,貌似知道的人不多或者用的人不多。GitHub 地址:
github.com/microsoft/vs-streamjsonrpc
StreamJsonRpc 可以在 Stream、WebSocket 或 System.IO.Pipelines 管道上工作,独立于底层传输。除了包含 JSON-RPC 规范所需的特性外,它额外还有如下优点:
请求取消
.NET 事件作为通知
动态客户端代理生成
支持紧凑的 MessagePack 二进制序列化
易于实现插件式架构的消息处理和格式化
使用 StreamJsonRpc 主要有四个基本步骤:建立 JSON-RPC 连接、发送 RPC 请求、接收 RPC 请求、断开连接。
这一篇主要介绍一些预备知识,下一篇将通过示例演示并详细介绍 StreamJsonRpc 的使用,敬请期待!
[2].github.com/microsoft/vs-streamjsonrpc
本文分享自微信公众号 - dotNET 跨平台(opendotnet)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与 “OSC 源创计划”,欢迎正在阅读的你也加入,一起分享。
.NET 开源项目 StreamJsonRpc 介绍 [中篇]
上一篇介绍了一些预备知识,包括 JSON-RPC 介绍和实现了 JSON-RPC 的 StreamJsonRpc 介绍,讲到了 StreamJsonRpc 可以通过 .NET 的 Stream 类和 WebSocket 类实现 JSON-RPC 协议的通信。本篇就先选择其中的 Stream 类来讲解,通过具体的示例讲解如何使用 StreamJsonRpc 实现 RPC 调用。
准备工作
先新建两个 Console 应用,分别命名为 StreamSample.Client 和 StreamSample.Server,并均添加 StreamJsonRpc 包引用。
mkdir StreamJsonRpcSamples # 创建目录
cd StreamJsonRpcSamples # 进入目录
dotnet new sln -n StreamJsonRpcSamples # 新建解决方案
dotnet new console -n StreamSample.Client # 建新客户端应用
dotnet new console -n StreamSample.Server # 新建服务端应用
dotnet sln add StreamSample.Client StreamSample.Server # 将应用添加到解决方案
dotnet add StreamSample.Client package StreamJsonRpc # 为客户端安装 StreamJsonRpc 包
dotnet add StreamSample.Server package StreamJsonRpc # 为服务端安装 StreamJsonRpc 包
上篇 提到了实现 JSON-RPC 通讯要经历四个步骤:建立连接、发送请求、接收请求、断开连接,其中发送请求和接收请求可以归为数据通讯,下面按照这几个步骤顺序来逐步讲解。
建立连接
使用 Stream 实现 JSON-RPC 协议的通讯,要求该 Stream 必须是一个全双工 Stream(可同时接收数据和发送数据)或才是一对半双工 Stream(本文不作讨论)。实现了全双工的 Stream 类在 .NET 中有 PipeStream
、NetworkStream
等,本示例用的是 NamedPipeClientStream
类和 NamedPipeServerStream
,前者用于客户端,后者用于服务端。
先看服务端代码示例:
int clientId = 1;
var stream = new NamedPipeServerStream("StringJsonRpc",
PipeDirection.InOut,
NamedPipeServerStream.MaxAllowedServerInstances,
PipeTransmissionMode.Byte,
PipeOptions.Asynchronous);
Console.WriteLine("等待客户端连接...");
await stream.WaitForConnectionAsync();
Console.WriteLine($"已与客户端 #{clientId} 建立连接");
这里使用了 NamedPipeServerStream
类,其第一个构造参数指定了该 Stream 管道的名称,方便客户端使用该名称查找。其它参数就不解释了,其各自的含义可以在你编写代码时通过智能提示了解。
Stream 实例通过 WaitForConnectionAsync
来等待一个客户端连接。由于该服务端可以连接多个客户端,这里使用自增长的 clientId
来标识区分它们。
再来看客户端代码示例:
var stream = new NamedPipeClientStream(".",
"StringJsonRpc",
PipeDirection.InOut,
PipeOptions.Asynchronous);
Console.WriteLine("正在连接服务器...");
await stream.ConnectAsync();
Console.WriteLine("已建立连接!");
和服务器类似,客户端使用的是 NamedPipeClientStream
类来建立连接,在其构造参数中需要指定服务端的地址(这里用了.
代表本机)和通讯管道的名称。Stream 实例通过 ConnectAsync
方法主动向服务器请求连接。
如果网络是通的,客户端和服务端就能成功建立连接。下面就要实现客户端和服务端之间的数据通讯了,即客户端发送请求和服务端接收并处理请求。
数据通讯
客户端与服务端建立连接后,数据不会无缘无故从一端流到另一端,要实现两端的数据通讯还需要先把通讯管道架设起来,在其两端设定对应的控制和处理程序。工程上这个听起来好像不简单,但对于 StreamJsonRpc 来说是件非常简单的事。最简单的方法是使用 JsonRpc 类的 Attach
静态方法来架设两端的 Stream 管道,该方法返回一个 JsonRpc 实例可以用来控制数据的通讯。
对于服务端,架设管道的同时还要为管道上的请求添加监听和对应的处理程序,比如定义一个名为 GreeterServer
的类来处理 “打招呼” 的请求:
public class GreeterServer
{
public string SayHello(string name)
{
Console.WriteLine($"收到【{name}】的问好,并回复了他");
return $"您好,{name}!";
}
}
然后实例化该类,把它传给 JsonRpc 类的 Attach
静态方法:
static async Task Main(string[] args)
{
...
_ = ResponseAsync(stream, clientId);
clientId++;
}
static Task ResponseAsync(NamedPipeServerStream stream, int clientId)
{
var jsonRpc = JsonRpc.Attach(stream, new GreeterServer());
return jsonRpc.Completion;
}
这里我们单独定义了一个 ResponseAsync
方法用来处理客户端请求,在 Main
函数中我们不用关心该方法返回的 Task 任务,所以使用了弃元。
对于客户端也是类似的,使用 JsonRpc 类的 Attach
静态方法来完成管道架设,并调用 JsonRpc 实例的 InvokeAsync
方法向服务端发送指定请求。代码示例如下:
...
Console.WriteLine("我是精致码农,开始向服务端问好...");
var jsonRpc = JsonRpc.Attach(stream);
var message = await jsonRpc.InvokeAsync<string>("SayHello", "精致码农");
Console.WriteLine($"来自服务端的响应:{message}");
这样就实现了客户端调用服务端的方法,但客户端需要知道服务端的方法签名。这里只是为示例演示,在实际情况中,客户端和服务端需要先约定好接口,这样客户端就可以面向接口实现强类型编程,不必关心服务端处理程序的具体信息。
注意到没,从建立连接到实现数据通讯,客户端和服务端都是对应的,而且使用的类和方法都是相似的。
断开连接
当客户端或服务器端在不需要发送请求或响应请求时,则可以调用 JsonRpc 实例的 Dispose 方法断开并释放连接。
jsonRpc.Dispose();
如果需要断开连接,一般是由客户端这边发起,比如对于控制台应用按 Ctrl + C 结束任务便会断开与服务端的连接。那服务端如何知道某个客户端断开了连接呢?可以手动等待 JsonRpc 实例的 Completion 任务完成,比如:
static async Task ResponseAsync(NamedPipeServerStream stream, int clientId)
{
var jsonRpc = JsonRpc.Attach(stream, new GreeterServer());
await jsonRpc.Completion;
Console.WriteLine($"客户端 #{clientId} 的已断开连接");
jsonRpc.Dispose();
await stream.DisposeAsync();
}
这里为了保险起见,我还手动把 stream 也释放掉了。
除了主动断开连接,客户端或服务器抛出未 catch 的异常也会致使连接中断,在实际情况中针对这种异常的连接中断可能需要编写重试机制,这里就不展开讨论了。
完整代码
以上为了讲解方便,代码只贴了与上下文相关的部分,最后我再把完整代码贴一下吧。
服务端 StreamSample.Server 下的 Program.cs:
客户端 StreamSample.Client 下的 Program.cs:
完整代码已放到 GitHub,地址为:
github.com/liamwang/StreamJsonRpcSamples
两个客户端和服务端一起运行的截图:
本篇总结
本文通过一个简单但完整的示例讲解了如何使用 StreamJsonRpc 来实现基于 JSON-RPC 协议的 RPC 调用。由于服务端和客户端都使用的是 StreamJsonRpc 库来实现的,所以在示例中感觉不到 JSON-RPC 协议带来的统一规范,也没看到具体的 JSON 格式的数据。这是因为 StreamJsonRpc 库都已经帮我们封装好了,两端都基于 C#,示例使用的也是简单的 Stream 方式,隐藏了我们不必关心的细节。其实只要符合 JSON-RPC 协议标准,C# 写的服务端也可以由其它语言实现的客户端来调用,反之亦然。
关注我一段时间的朋友都知道,我的文章篇幅一般不会太长,主要是方便大家利用零碎时间把它一次性看完。StreamJsonRpc 的使用远不止本文讲的这些,比如还有基于 WebSocket 进行数据传输的方式。来想通过两篇讲完,但讲了一半就已经超出了预期的篇幅长度。所以我把本文定为 [中篇],如果有时间我会继续写 [下篇],下篇主要会讲 StreamJsonRpc + WebSocket 的使用,并会尽量以更贴合实际应用场景的示例来讲解。