ASP.NET Core SignalR (九):SignalR .NET 客户端

摘要:
本章中的示例代码是使用SignalR的WPF应用程序。NET客户端类库。Microsoft.AspNetCore。安装SignalR需要SignalR。NET客户端包。NET客户端客户端包连接到SignalRhubs。在Visual Studio中,为了安装SignalR客户端类库,请在PMC窗口中运行以下命令:install PackageMicrosoft.AspNetCore.SignalR。如果您通过。NET Core脚手架,您可以通过以下命令安装它:dotnetaddpackageMicrosoft.AspNetCore。信号R客户端连接到中心。要建立连接,我们可以创建HubConnectionBuilder并调用Build。通过将任何HubConnectionBuilder方法插入Build来配置所需的选项。connection.Reconnecting+=error=˃{Debug.Assert;//通知用户连接已断开,而客户端正在重新连接。//开始队列删除消息。returnTask.CompleteTask;};如果在客户端的前4次尝试中连接成功,HubConnection将转换回Connected状态并触发Reconnected事件。
此为系列文章,对MSDN ASP.NET Core SignalR 的官方文档进行系统学习与翻译。其中或许会添加本人对 ASP.NET Core 的浅显理解

        ASP.NET Core SignalR .NET 类库允许你从.NET app 和 SignalR hubs 进行通信。

        本章的示例代码是一个WPF 应用程序,其使用了SignalR .NET客户端类库。

安装SignalR .NET 客户端包

       .NET 客户端需要Microsoft.AspNetCore.SignalR.Client 包来连接到 SignalR hubs。在Visual studio 中,为了安装SignalR 客户端类库,在PMC窗口中运行如下命令:

Install-Package Microsoft.AspNetCore.SignalR.Client

       如果通过.NET Core 脚手架,可以通过如下命令来安装:

dotnet add package Microsoft.AspNetCore.SignalR.Client

连接到一个 中心

       为了建立连接,我们可以创建一个 HubConnectionBuilder 并调用 Build。中心 的 URL,协议,传送类型,日志级别,头信息,以及其他选项都可以在构建连接的时候被配置。通过插入任意的HubConnectionBuilder 方法到Build来配置所需的选项。然后使用 StartAsync 来启动这个连接。

using System;
using System.Threading.Tasks;
using System.Windows;
using Microsoft.AspNetCore.SignalR.Client;

namespace SignalRChatClient
{
    public partial class MainWindow : Window
    {
        HubConnection connection;
        public MainWindow()
        {
            InitializeComponent();

            connection = new HubConnectionBuilder()
                .WithUrl("http://localhost:53353/ChatHub")
                .Build();

            connection.Closed += async (error) =>
            {
                await Task.Delay(new Random().Next(0,5) * 1000);
                await connection.StartAsync();
            };
        }

        private async void connectButton_Click(object sender, RoutedEventArgs e)
        {
            connection.On<string, string>("ReceiveMessage", (user, message) =>
            {
                this.Dispatcher.Invoke(() =>
                {
                   var newMessage = $"{user}: {message}";
                   messagesList.Items.Add(newMessage);
                });
            });

            try
            {
                await connection.StartAsync();
                messagesList.Items.Add("Connection started");
                connectButton.IsEnabled = false;
                sendButton.IsEnabled = true;
            }
            catch (Exception ex)
            {
                messagesList.Items.Add(ex.Message);
            }
        }

        private async void sendButton_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                await connection.InvokeAsync("SendMessage", 
                    userTextBox.Text, messageTextBox.Text);
            }
            catch (Exception ex)
            {                
                messagesList.Items.Add(ex.Message);                
            }
        }
    }
}

处理丢失的连接

       自动重连

       使用 HubConnectionBuilder 上的WithAutomaticReconnect 方法,HubConnection 可以被配置为自动重连。默认情况下它不会自动重连。

HubConnection connection= new HubConnectionBuilder()
    .WithUrl(new Uri("http://127.0.0.1:5000/chatHub"))
    .WithAutomaticReconnect()
    .Build();

       没有任何参数,WithAutomaticReconnect 配置客户端依次等待 0,2,10,30秒来尝试进行连接,如果4次连接尝试均失败,那么便会停止连接。

       在开始任何重连尝试之前,HubConnection 会过渡到HubConnectionState.Reconnecting状态并触发Reconnecting 事件。这便提供了一个时机,我们可以在这个事件中警告用户连接已经丢失,并禁用掉UI元素。非交互性app可以将消息加入队列或者丢弃消息。

connection.Reconnecting += error =>
{
    Debug.Assert(connection.State == HubConnectionState.Reconnecting);

    // Notify users the connection was lost and the client is reconnecting.
    // Start queuing or dropping messages.

    return Task.CompletedTask;
};

       如果在客户端的前4次尝试之内便成功连接的话,HubConnection 会过渡回 Connected 状态并触发Reconnected事件。这也提供了一个时机来通知用户连接已经被重新建立,并入队任何队列消息。

  因为连接看起来对于服务端来说是完全新的,因而一个新的ConnectionId会被提供给Reconnected事件处理器。

  注意:如果HubConnection被配置为skip negotiation,那么Reconnected事件处理器的connectionId参数将会为null。

connection.Reconnected += connectionId =>
{
    Debug.Assert(connection.State == HubConnectionState.Connected);

    // Notify users the connection was reestablished.
    // Start dequeuing messages queued while reconnecting if any.

    return Task.CompletedTask;
};

        WithAutomaticReconnect() 方法不会配置HubConnection重试最初的启动失败,因此,启动失败需要进行手动处理。

public static async Task<bool> ConnectWithRetryAsync(HubConnection connection, CancellationToken token)
{
    // Keep trying to until we can start or the token is canceled.
    while (true)
    {
        try
        {
            await connection.StartAsync(token);
            Debug.Assert(connection.State == HubConnectionState.Connected);
            return true;
        }
        catch when (token.IsCancellationRequested)
        {
            return false;
        }
        catch
        {
            // Failed to connect, trying again in 5000 ms.
            Debug.Assert(connection.State == HubConnectionState.Disconnected);
            await Task.Delay(5000);
        }
    }
}

       如果客户端在其前4次尝试之内(自动重连)没有成功重连,那么HubConnection 会过渡到 Disconnected 状态并触发Closed 状态。这提供了一个机会我们可以尝试手动重启连接或者通知用户连接已经永久丢失。

connection.Closed += error =>
{
    Debug.Assert(connection.State == HubConnectionState.Disconnected);

    // Notify users the connection has been closed or manually try to restart the connection.

    return Task.CompletedTask;
};

        在断开或者更改重连定时之前,为了配置自定义的重连尝试次数,WithAutomaticReconnect方法接收一个数字数组作为参数,其以毫秒数表示了在各个重连尝试之前需要等待的延迟。

HubConnection connection= new HubConnectionBuilder()
    .WithUrl(new Uri("http://127.0.0.1:5000/chatHub"))
    .WithAutomaticReconnect(new[] { TimeSpan.Zero, TimeSpan.Zero, TimeSpan.FromSeconds(10) })
    .Build();

    // .WithAutomaticReconnect(new[] { TimeSpan.Zero, TimeSpan.FromSeconds(2), TimeSpan.FromSeconds(10), TimeSpan.FromSeconds(30) }) yields the default behavior.

       之前的代码配置HubConnection 在连接丢失之后立即尝试重连。这和默认的配置是一样的。

       如果第一次重连尝试失败了,第二次重连尝试在等待2秒之后也会立即开始,就如通它在默认配置中的一样。

       如果第二次重连尝试失败了,第三次重连尝试会在10秒内开始,其同样和默认配置保持一致。

       在第三次重连尝试失败之后,便会停止连接,自定义行为便相异于默认行为。在gxeng默认配置中,在30秒之后还会有另一次重连尝试。

       如果你想对定时以及自动重连的次数具有更多的控制,WithAutomaticReconnect 也可以接收一个实现了IRetryPolicy接口的对象作为参数,其具有一个单独的名为NextRetryDelay的方法。 

       NextRetryDelay 具有一个单独的类型为RetryContext 的参数。RetryContext 具有三个属性,PreviousRetryCount,ElapsedTime ,RetryReason,它们分别是 long,TimeSpan,Exception类型。在第一次重连尝试之前,PreviousRetryCount和ElapsedTime都会是 0,而RetryReason会是导致连接丢失的异常。在每一次失败的重试尝试之后,PreviousRetryCount 会自增加1,ElapsedTime会被更新以反映到现在为止花费在重连上的时间。RetryReason将会是导致上一次重连尝试失败的原因。

       NextRetryDelay 要么返回一个表示在下一次重连尝试之前需要等待的时间的TimeSpan,要么返回null,返回null时,表示HubConnection 应该停止重连。

public class RandomRetryPolicy : IRetryPolicy
{
    private readonly Random _random = new Random();

    public TimeSpan? NextRetryDelay(RetryContext retryContext)
    {
        // If we've been reconnecting for less than 60 seconds so far,
        // wait between 0 and 10 seconds before the next reconnect attempt.
        if (retryContext.ElapsedTime < TimeSpan.FromSeconds(60))
        {
            return TimeSpan.FromSeconds(_random.NextDouble() * 10);
        }
        else
        {
            // If we've been reconnecting for more than 60 seconds so far, stop reconnecting.
            return null;
        }
    }
}
HubConnection connection = new HubConnectionBuilder()
    .WithUrl(new Uri("http://127.0.0.1:5000/chatHub"))
    .WithAutomaticReconnect(new RandomRetryPolicy())
    .Build();

       除此之外,你可以写代码来手动连接你的客户端,如同下一章节所演示的那样。

       手动重连

       使用Closed事件来响应丢失的连接。比如,或许你想自动重连。

       Closed 事件需要一个返回Task的委托,其允许不使用 async void 的 异步代码。为了在一个异步运行的Closed 事件处理器中满足委托签名,可以返回Task.CompletedTask。

connection.Closed += (error) => {
    // Do your close logic.
    return Task.CompletedTask;
};

       支持异步的主要原因是这样你可以重启连接。启动连接是一个异步动作。

       在重启连接的 Closed 处理程序中,考虑等待一些随机延迟以防止服务器过载,如下示例所示:

connection.Closed += async (error) =>
{
    await Task.Delay(new Random().Next(0,5) * 1000);
    await connection.StartAsync();
};

从客户端调用 中心 方法

       InvokeAsync 可以调用 中心 的方法。将 中心 方法的名称和方法中定义的参数传递给 InvokeAsync。SignalR是异步的,因此,当调用时,使用async 和 await关键字。

await connection.InvokeAsync("SendMessage", 
    userTextBox.Text, messageTextBox.Text);

      InvokeAsync 方法返回一个Task,其将在服务端方法返回的时候完成。如果有任何返回值的话,都应该作为Task 的结果返回。服务端方法返回的任何异常都会产生一个有错误的Task。使用await 标记来等待服务端方法完成,使用 try...catch标签来处理异常。

      SendAsync 方法返回一个Task,其会在消息被送到服务端时完成。因为这个方法不会等待服务端方法完成,所以其没有提供返回值。当发送消息时客户端抛出的任何异常均会产生一个错误的Task。同样,使用try...catch 来处理错误。

      注意:如果你正在以无服务模式 使用Azure SignalR 服务,那么你不能从客户端调用 hubs 方法。更多信息,请参考SignalR Service documentation

从 中心 调用客户端方法

      在构建连接之后,启动连接之前,使用 connection.On 定义 中心 调用的方法。

connection.On<string, string>("ReceiveMessage", (user, message) =>
{
    this.Dispatcher.Invoke(() =>
    {
       var newMessage = $"{user}: {message}";
       messagesList.Items.Add(newMessage);
    });
});

       当服务端代码使用 SendAsync 方法时,如上在connection.On 中的代码会被运行。

public async Task SendMessage(string user, string message)
{
    await Clients.All.SendAsync("ReceiveMessage", user,message);
}

错误处理及日志

       使用try-catch语句来处理错误。在 一个错误产生后,检查异常对象以决定采取合适的动作。

try
{
    await connection.InvokeAsync("SendMessage", 
        userTextBox.Text, messageTextBox.Text);
}
catch (Exception ex)
{                
    messagesList.Items.Add(ex.Message);                
}

额外资源

        

免责声明:文章转载自《ASP.NET Core SignalR (九):SignalR .NET 客户端》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇使用 ASP.NET 一般处理程序或 WebService 返回 JSONgitlab如何从Github导入项目下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

一、在 ASP.NET Core 中使用 SignalR

一、介绍 SignalR是一个用于实现实时网站的 Microsoft .NET 库。它使用多种技术来实现服务器与客户端间的双向通信,服务器可以随时将消息推送到连接的客户端。 https://docs.microsoft.com/zh-cn/aspnet/core/tutorials/signalr?tabs=visual-studio&view=a...

ASP.NET Core SignalR (六):在SignalR中管理用户和分组

此为系列文章,对MSDN ASP.NET Core SignalR 的官方文档进行系统学习与翻译。其中或许会添加本人对 ASP.NET Core 的浅显理解。        SignalR 允许消息被发送给关联给特定用户的所有连接,以及命名的连接分组。 SignalR中的用户        SignalR 允许你将消息发送给关联给特定用户的所有连接。默认情...

SignalR 2.0 系列:SignalR的服务器广播

英文渣水平,大伙凑合着看吧…… 这是微软官方SignalR 2.0教程Getting Started with ASP.NET SignalR 2.0系列的翻译,这里是第八篇:SignalR的服务器广播 原文:Tutorial: Server Broadcast with SignalR 2.0 概述 VS可以通过Microsoft.AspNet.Sign...

C# ASP.NET MVC 之 SignalR 学习 实时数据推送显示 配合 Echarts 推送实时图表

本文主要是我在刚开始学习 SignalR 的技术总结,网上找的学习方法和例子大多只是翻译了官方给的一个例子,并没有给出其他一些经典情况的示例,所以才有了本文总结,我在实现推送简单的数据后,就想到了如何去推送复杂的数据,以及推送一个实时的图表数据,文本为我原创,转载请注明出处:Richard.Hu,先上一堆乱七八糟的说明先: SignalR的官方地址是: h...

Signalr入门系列:Signalr简介(二)

一、SignalR和WebSocket SignalR是在WebSocket的基础上进一步的封装,可以实现除了WebSocket原有功能上更多的功能,例如回退到其他transports,修改应用程序以更新WebSocket implementations。SignalR可以自动判断传输方式,在可以用的情况下使用新的WebSocket传输,并在必要时使用旧的...

netcore3.1 webapi使用signalR

前言   今天尝试了一下signalR,感觉还不错,因为暂时用不到,就写一篇博文来记录搭建过程,以免以后给忘了,基于官方文档写的,不过官方没有webapi调用例子,就自己写了一下,大神勿喷 使用 1.创建一个netcore3.1 webapi程序,nuget引用一下Microsoft.AspNetCore.SignalR,我这里是1.1.0版本 2.创建...