.NET 开源项目 StreamJsonRpc 介绍[中篇]
阅读本文大概需要 11 分钟。

上一篇介绍了一些预备知识,包括 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:
class Program
{
static async Task Main(string[] args)
{
int clientId = 1;
while (true)
{
var stream = new NamedPipeServerStream("StringJsonRpc",
PipeDirection.InOut,
NamedPipeServerStream.MaxAllowedServerInstances,
PipeTransmissionMode.Byte,
PipeOptions.Asynchronous);
Console.WriteLine("等待客户端连接...");
await stream.WaitForConnectionAsync();
Console.WriteLine($"已与客户端 #{clientId} 建立连接");
_ = ResponseAsync(stream, clientId);
clientId++;
}
}
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();
}
}
public class GreeterServer
{
public string SayHello(string name)
{
Console.WriteLine($"收到【{name}】的问好,并回复了他");
return $"您好,{name}!";
}
}
客户端 StreamSample.Client 下的 Program.cs:
class Program
{
static async Task Main(string[] args)
{
var stream = new NamedPipeClientStream(".",
"StringJsonRpc",
PipeDirection.InOut,
PipeOptions.Asynchronous);
Console.WriteLine("正在连接服务器...");
await stream.ConnectAsync();
Console.WriteLine("已建立连接!");
Console.WriteLine("我是精致码农,开始向服务端问好...");
var jsonRpc = JsonRpc.Attach(stream);
var message = await jsonRpc.InvokeAsync<string>("SayHello", "精致码农");
Console.WriteLine($"来自服务端的响应:{message}");
Console.ReadKey();
}
}
完整代码已放到 GitHub,地址为:
github.com/liamwang/StreamJsonRpcSamples
两个客户端和服务端一起运行的截图:

本篇总结
本文通过一个简单但完整的示例讲解了如何使用 StreamJsonRpc 来实现基于 JSON-RPC 协议的 RPC 调用。由于服务端和客户端都使用的是 StreamJsonRpc 库来实现的,所以在示例中感觉不到 JSON-RPC 协议带来的统一规范,也没看到具体的 JSON 格式的数据。这是因为 StreamJsonRpc 库都已经帮我们封装好了,两端都基于 C#,示例使用的也是简单的 Stream 方式,隐藏了我们不必关心的细节。其实只要符合 JSON-RPC 协议标准,C# 写的服务端也可以由其它语言实现的客户端来调用,反之亦然。
关注我一段时间的朋友都知道,我的文章篇幅一般不会太长,主要是方便大家利用零碎时间把它一次性看完。StreamJsonRpc 的使用远不止本文讲的这些,比如还有基于 WebSocket 进行数据传输的方式。来想通过两篇讲完,但讲了一半就已经超出了预期的篇幅长度。所以我把本文定为[中篇],如果有时间我会继续写[下篇],下篇主要会讲 StreamJsonRpc + WebSocket 的使用,并会尽量以更贴合实际应用场景的示例来讲解。
.NET 开源项目 StreamJsonRpc 介绍[中篇]的更多相关文章
- .NET 开源项目 StreamJsonRpc 介绍[下篇]
阅读本文大概需要 9 分钟. 大家好,这是 .NET 开源项目 StreamJsonRpc 介绍的最后一篇.上篇介绍了一些预备知识,包括 JSON-RPC 协议介绍,StreamJsonRpc 是一个 ...
- .NET 开源项目 StreamJsonRpc 介绍
StreamJsonRpc 是一个实现了 JSON-RPC 通信协议的开源 .NET 库,在介绍 StreamJsonRpc 之前,我们先来了解一下 JSON-RPC. JSON-RPC 介绍 JSO ...
- 工业通信的开源项目 HslCommunication 介绍
前言: 本项目的孵化说来也是机缘巧合的事,本人于13年杭州某大学毕业后去了一家大型的国企工作,慢慢的走上了工业软件,上位机软件开发的道路.于14年正式开发基于windows的软件,当时可选的技术栈就是 ...
- 强大的HTTP包装开源项目ASIHTTPRequest介绍
ASIHTTPRequest 是一个直接在CFNetwork上做的开源项目,提供了一个比官方更方便更强大的HTTP网络传输的封装.它的特色功能如下: 1,下载的数据直接保存到内存或文件系统里 2,提供 ...
- 十款不容错过的Swift iOS开源项目及介绍
1.十款不容错过的Swift iOS开源项目. http://www.csdn.net/article/2014-10-16/2822083-swift-ios-open-source-project ...
- .NET 开源项目 Anet 介绍
使用 Anet 有一段时间了,已经在我的个人网站(如 bookist.cc)投入使用,目前没有发现什么大问题,所以才敢写篇文章向大家介绍. GitHub 地址:https://github.com/a ...
- .NET 开源项目 Polly 介绍
今天介绍一个 .NET 开源库:Polly,它是支持 .NET Core 的,目前在 GitHub 的 Star 数量已经接近 5 千,它是一个强大且实用的 .NET 库. Polly 介绍 官方对 ...
- 开源项目android-uitableview介绍
在iOS应用中,UITableView应该是使用率最高的视图之一了.iPod.时钟.日历.备忘录.Mail.天气.照片.电话.短信. Safari.App Store.iTunes.Game Cent ...
- 08_android入门_android-async-http开源项目介绍及用法
android-async-http开源项目可以是我们轻松的获取网络数据或者向server发送数据.使用起来很easy,关于android-async-http开源项目的介绍内容来自于官方:http: ...
随机推荐
- java实现数组转置
** 数组转置** 编写程序将2行3列的数组行列置换复制给3行2列的数组(即数组的转置).已经写了如下代码,请完善之: class y{ public static void main(String[ ...
- 【JAVA习题二十九】809*??=8*??+9*??+1 其中??代表的两位数,8*??的结果为两位数,9*??的结果为3位数。求??代表的两位数,及809*??后的结果。
package erase; public class 八九与问好两位数的乘积和 { public static void main(String[] args) { int m,n;//m十位,n个 ...
- Linux的文件系统及文件缓存知识点整理
Linux的文件系统 文件系统的特点 文件系统要有严格的组织形式,使得文件能够以块为单位进行存储. 文件系统中也要有索引区,用来方便查找一个文件分成的多个块都存放在了什么位置. 如果文件系统中有的文件 ...
- bfs算法总结
BFS(广度优先搜索) 类似于树的按层遍历 常用实现方法:队列 模板: 注意: 只能用来求解无权图的最短路径问题 队列:用来存储每一层便利得到的节点 标记:对于遍历过的结点,应将其标记,以防重复访问 ...
- 【String注解驱动开发】如何按照条件向Spring容器中注册bean?这次我懂了!!
写在前面 当bean是单实例,并且没有设置懒加载时,Spring容器启动时,就会实例化bean,并将bean注册到IOC容器中,以后每次从IOC容器中获取bean时,直接返回IOC容器中的bean,不 ...
- Java操作符,<<、>>等
数学意义:在数字没有溢出的前提下,对于正数和负数,左移一位都相当于乘以2的1次方,左移n位就相当于乘以2的n次方. 计算:3 << 2 3 << 2,则是将数字3左移2位 1. ...
- CDN百科第四讲 | 如何优雅地在云上“摆摊”——做直播带货,你不得不关注的技术
最近,国家政策开始鼓励“地摊经济”,一时间各家企业平台纷纷推出地摊扶持政策,地摊概念股顺势大涨,地摊生态及配套商品也开始走俏,甚至在网络上也涌现出各种“新摊主速成攻略”,万亿的烟火经济俨然已经走上风口 ...
- Razor 视图
Razor 视图 关于视图引擎 视图引擎简单理解就是能够支持对视图的解析,在 ASP.NET MVC 中,视图引擎的作用就是把视图处理成浏览器能够执行的 HTML 代码,不同的视图引擎,语法规则不一样 ...
- Python SimpleHTTPServer (python3 -m http.server 6789)
搭建FTP,或者是搭建网络文件系统,这些方法都能够实现Linux的目录共享.但是FTP和网络文件系统的功能都过于强大,因此它们都有一些不够方便的地方.比如你想快速共享Linux系统的某个目录给整个项目 ...
- 利用bat文件打开浏览器指定网页,提示 windows找不到chrome.exe。请确定文件名是否正确,再试一次 的解决经历
1.在网上找到一些bat命令,原来电脑可以正常使用,效果就是执行后打开谷歌浏览器并全屏展示某网页,但是在昨天换个一个电脑后发现不能用了,提示以下截图的错误 2.找了半天发现问题所在: 把chrom ...