日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > asp.net >内容正文

asp.net

.NET 开源项目 StreamJsonRpc 介绍[下篇]

發布時間:2023/12/4 asp.net 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 .NET 开源项目 StreamJsonRpc 介绍[下篇] 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

閱讀本文大概需要 9 分鐘。

大家好,這是 .NET 開源項目 StreamJsonRpc 介紹的最后一篇。上篇介紹了一些預備知識,包括 JSON-RPC 協議介紹,StreamJsonRpc 是一個實現了 JSON-RPC 協議的庫,它基于 Stream、WebSocket 和自定義的全雙工管道傳輸。中篇通過示例講解了 StreamJsonRpc 如何使用全雙工的 Stream 作為傳輸管道實現 RPC 通訊。本篇(下篇)將繼續通過示例講解如何基于 WebSocket 傳輸管道實現 RPC 通訊。

準備工作

為了示例的完整性,本文示例繼續在中篇創建的示例基礎上進行。該示例的 GitHub 地址為:

github.com/liamwang/StreamJsonRpcSamples

我們繼續添加三個項目,一個是名為 WebSocketSample.Client 的 Console 應用,一個是名為 WebSocketSample.Server 的 ASP.NET Core 應用,還有一個名為 Contract 的契約類庫(和 gRPC 類似)。

你可以直接復制并執行下面的命令一鍵完成大部分準備工作:

dotnet new console -n WebSocketSample.Client # 建新客戶端應用 dotnet new webapi -n WebSocketSample.Server # 新建服務端應用 dotnet new classlib -n Contract # 新建契約類庫 dotnet sln add WebSocketSample.Client WebSocketSample.Server Contract # 將項目添加到解決方案 dotnet add WebSocketSample.Client package StreamJsonRpc # 為客戶端安裝 StreamJsonRpc 包 dotnet add WebSocketSample.Server package StreamJsonRpc # 為服務端安裝 StreamJsonRpc 包 dotnet add WebSocketSample.Client reference Contract # 添加客戶端引用 Common 引用 dotnet add WebSocketSample.Server reference Contract # 添加服務端引用 Common 引用

為了把重點放在實現上,這次我們依然以一個簡單的功能作為示例。該示例實現客戶端向服務端發送一個問候數據,然后服務端響應一個消息。為了更貼合實際的場景,這次使用強類型進行操作。為此,我們在 Contract 項目中添加三個類用來約定客戶端和服務端通訊的數據結構和接口。

用于客戶端發送的數據的 HelloRequest 類:

public?class?HelloRequest {public?string Name { get; set; } }

用于服務端響應的數據的 HelloResponse 類:

public?class?HelloResponse {public?string Message { get; set; } }

用于約定服務端和客戶端行為的 IGreeter 接口:

public?interface?IGreeter {Task<HelloResponse> SayHelloAsync(HelloRequest request); }

接下來和中篇一樣,通過建立連接、發送請求、接收請求、斷開連接這四個步驟演示和講解一個完整的基于 WebSocket 的 RPC 通訊示例。

建立連接

上一篇講到要實現 JSON-RPC 協議的通訊,要求傳輸管道必須是全雙工的。而 WebSocket 就是標準的全雙工通訊,所以自然可以用來實現 JSON-RPC 協議的通訊。.NET 本身就有現成的 WebSocket 實現,所以在建立連接階段和 StreamJsonRpc 沒有關系。我們只需要把 WebSocket 通訊管道架設好,然后再使用 StreamJsonRpc 來發送和接收請求即可。

客戶端使用 WebSocket 建立連接比較簡單,使用?ClientWebSocket?來實現,代碼如下:

using (var webSocket = new ClientWebSocket()) {Console.WriteLine("正在與服務端建立連接...");var uri = new Uri("ws://localhost:5000/rpc/greeter");await webSocket.ConnectAsync(uri, CancellationToken.None);Console.WriteLine("已建立連接"); }

服務端建立 WebSocket 連接最簡單的方法就是使用 ASP.NET Core,借助 Kestrel 和 ASP.NET Core 的中間件機制可以輕松搭建基于 WebSocket 的 RPC 服務。只要簡單的封裝還可以實現同一套代碼同時提供 RPC 服務和 Web API 服務。

首先在服務端項目的 Startup.cs 類的?Configure?方法中引入 WebSocket 中間件:

public?void?Configure(IApplicationBuilder app, IWebHostEnvironment env) {app.UseRouting();app.UseWebSockets(); // 增加此行,引入 WebSocket 中間件app.UseEndpoints(endpoints =>{endpoints.MapControllers();}); }

再新建一個 Controller 并定義一個 Action 用來路由映射 WebSocket 請求:

public?class?RpcController : ControllerBase {...[Route("/rpc/greeter")]public?async Task<IActionResult> Greeter(){if (!HttpContext.WebSockets.IsWebSocketRequest){return?new BadRequestResult();}var socket = await HttpContext.WebSockets.AcceptWebSocketAsync();...} }

這里的 Greeter 提供的服務既能接收 HTTP 請求也能接收 WebSocket 請求。HttpContext?中的?WebSockets?屬性是一個?WebSocketManager?對象,它可以用來判斷當前請求是否為一個 WebSocket 請求,也可以用來等待和接收 WebSocket 連接,即上面代碼中的?AcceptWebSocketAsync?方法。另外客戶端的 WebSocket 的 Uri 路徑需要與 Router 指定的路徑對應。

連接已經建立,現在到了 StreamJsonRpc 發揮作用的時候了。

發送請求

客戶端通過 WebSocket 發送請求的方式和前一篇講的 Stream 方式是一樣的。還記得前一篇講到的 JsonRpc 類的 Attach 靜態方法嗎?它告訴 StreamJsonRpc 如何傳輸數據,并返回一個用于調用 RPC 的客戶端,它除了可以接收 Stream 參數外還有多個重載方法。比如:

public?static T Attach<T>(Stream stream); public?static T Attach<T>(IJsonRpcMessageHandler handler);

第二個重載方法可以實現更靈活的 Attach 方式,你可以 Attach 一個交由 WebSocket 傳輸數據的管道,也可以 Attach 給一個自定義實現的 TCP 全雙工傳輸管道(此方式本文不講,但文末會直接給出示例)。現在我們需要一個實現了?IJsonRpcMessageHandler?接口的處理程序,StreamJsonRpc 已經實現好了,它是?WebSocketMessageHandler?類。通過 Attach 該實例,可以拿到一個用于調用 RPC 服務的對象。代碼示例如下:

Console.WriteLine("開始向服務端發送消息..."); var messageHandler = new WebSocketMessageHandler(webSocket); var greeterClient = JsonRpc.Attach<IGreeter>(messageHandler); var request = new HelloRequest { Name = "精致碼農" }; var response = await greeterClient.SayHelloAsync(request); Console.WriteLine($"收到來自服務端的響應:{response.Message}");

你會發現,定義客戶端和服務端契約的好處是可以實現強類型編程。接下來看服務端如何接收并處理客戶端發送的消息。

接收請求

和前一篇一樣,我們先定義一個 GreeterServer 類用來處理接收到的客戶端消息。

public?class?GreeterServer : IGreeter {private?readonly ILogger<GreeterServer> _logger;public?GreeterServer(ILogger<GreeterServer> logger){_logger = logger;}public Task<HelloResponse> SayHelloAsync(HelloRequest request){_logger.LogInformation("收到并回復了客戶端消息");return Task.FromResult(new HelloResponse{Message = $"您好, {request.Name}!"});} }

同樣,WebSocket 服務端也需要使用 Attach 來告訴 StreamJsonRpc 數據如何通訊,而且使用的也是?WebSocketMessageHandler?類,方法與客戶端類似。在前一篇中,我們 Attach 一個 Stream 調用的方法是:

public?static JsonRpc Attach(Stream stream, object? target = null);

同理,我們推測應該也有一個這樣的靜態重載方法:

public?static JsonRpc Attach(IJsonRpcMessageHandler handler, object? target = null);

可惜,StreamJsonRpc 并沒有提供這個靜態方法。既然 Attach 方法返回的是一個 JsonRpc 對象,那我們是否可以直接實例化該對象呢?查看該類的定義,我們發現是可以的,而且有我們需要的構造函數:

public?JsonRpc(IJsonRpcMessageHandler messageHandler, object? target);

接下來就簡單了,一切和前一篇的 Stream 示例都差不多。在 RpcController 的 Greeter Action 中實例化一個 JsonRpc,然后開啟消息監聽。

public?class?RpcController : ControllerBase {private?readonly ILogger<RpcController> _logger;private?readonly GreeterServer _greeterServer;public?RpcController(ILogger<RpcController> logger, GreeterServer greeterServer){_logger = logger;_greeterServer = greeterServer;}[Route("/rpc/greeter")]public?async Task<IActionResult> Greeter(){if (!HttpContext.WebSockets.IsWebSocketRequest){return?new BadRequestResult();}_logger.LogInformation("等待客戶端連接...");var socket = await HttpContext.WebSockets.AcceptWebSocketAsync();_logger.LogInformation("已與客戶端建立連接");var handler = new WebSocketMessageHandler(socket);using (var jsonRpc = new JsonRpc(handler, _greeterServer)){_logger.LogInformation("開始監聽客戶端消息...");jsonRpc.StartListening();await jsonRpc.Completion;_logger.LogInformation("客戶端斷開了連接");}return?new EmptyResult();} }

看起來和我們平時寫 Web API 差不多,區別僅僅是對請求的處理方式。但需要注意的是,WebSocket 是長連接,如果客戶端沒有事情可以處理了,最好主動斷開與服務端的連接。如果客戶客戶沒有斷開連接,執行的上下文就會停在?await jsonRpc.Completion?處。

斷開連接

通常斷開連接是由客戶端主動發起的,所以服務端不需要做什么處理。服務端響應完消息后,只需使用?jsonRpc.Completion?等待客戶端斷開連接即可,上一節的代碼示例中已經包含了這部分代碼,就不再累述了。如果特殊情況下服務端需要斷開連接,調用 JsonRpc 對象的 Dispose 方法即可。

不管是 Stream 還是 WebSocket,其客戶端對象都提供了 Close 或 Dispose 方法,連接會隨著對象的釋放自動斷開。但最好還是主動調用 Close 方法斷開連接,以確保服務端收到斷開的請求。對于 ClientWebSocket,需要調用 CloseAsync 方法。客戶端完整示例代碼如下:

static?async Task Main(string[] args) {using (var webSocket = new ClientWebSocket()){Console.WriteLine("正在與服務端建立連接...");var uri = new Uri("ws://localhost:5000/rpc/greeter");await webSocket.ConnectAsync(uri, CancellationToken.None);Console.WriteLine("已建立連接");Console.WriteLine("開始向服務端發送消息...");var messageHandler = new WebSocketMessageHandler(webSocket);var greeterClient = JsonRpc.Attach<IGreeter>(messageHandler);var request = new HelloRequest { Name = "精致碼農" };var response = await greeterClient.SayHelloAsync(request);Console.WriteLine($"收到來自服務端的響應:{response.Message}");Console.WriteLine("正在斷開連接...");await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "斷開連接", CancellationToken.None);Console.WriteLine("已斷開連接");}Console.ReadKey(); }

在實際項目中可能還需要因異常而斷開連接的情況做處理,比如網絡不穩定可能導致連接中斷,這種情況可能需要加入重試機制。

運行示例

由于服務端使用的是 ASP.NET Core 模板,VS 默認使用 IIS Express 啟動,啟動后會自動打開網頁,這樣看不到 Console 的日志信息。所以需要把服務端項目 WebSocketSample.Server 的啟動方式改成自啟動。

另外,為了更方便地同時運行客戶端和服務端應用,可以把解決方案設置成多啟動。右鍵解決方案,選擇“Properties”,把對應的項目設置“Start”即可。

如果你用的是 VS Code,也是支持多啟動調試的,具體方法你自行 Google。如果你用的是?dotnet run?命令運行項目可忽略以上設置。

項目運行后的截圖如下:

你也可以自定義實現 TCP 全雙工通訊管道,但比較復雜而且也很少這么做,所以就略過不講了。但我在 GitHub 的示例代碼也放了一個自定義全雙工管道實現的示例,感興趣的話你可以克隆下來研究一下。

該示例運行截圖:

本篇總結

本文通過示例演示了如何使用 StreamJsonRpc 基于 WebSocket 數據傳輸實現 JSON-RPC 協議的 RPC 通訊。其中客戶端和服務端有共同的契約部分,實現了強類型編程。通過示例我們也清楚了 StreamJsonRpc 這個庫為了實現 RPC 通訊做了哪些工作,其實它就是在現有傳輸管道(Stream、WebSocket 和 自定義 TCP 連接)上進行數據通訊。正如前一篇所說,由于 StreamJsonRpc 把大部分我們不必要知道的細節做了封裝,所以在示例中感覺不到 JSON-RPC 協議帶來的統一規范,也沒看到具體的 JSON 格式的數據。其實只要遵循了 JSON-RPC 協議實現的客戶端或服務端,不管是用什么語言實現,都是可以互相通訊的。

希望這三篇關于 StreamJsonRpc 的介紹能讓你有所收獲,如果你在工作中計劃使用 StreamJsonRpc,這幾篇文章包括示例代碼應該有值得參考的地方。

總結

以上是生活随笔為你收集整理的.NET 开源项目 StreamJsonRpc 介绍[下篇]的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。