-
- 项目
- 2022/11/29
通过 ASP.NET Core SignalR .NET 客户端库可以从 .NET 应用与 SignalR 中心进行通信。
本文中的代码示例是使用 .ASP.NET Core SignalR .NET 客户端的 WPF 应用。
安装 SignalR .NET 客户端包
Microsoft.AspNetCore.SignalR..NET 客户端需要客户端包才能连接到SignalR中心。
若要安装该客户端库,请在“包管理器控制台”窗口中运行以下命令:
PowerShellInstall-Package Microsoft.AspNetCore.SignalR.Client
连接到中心
若要建立连接,请创建
C#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); } } } }
处理丢失的连接
自动重新连接
可以将 HubConnection 配置为对 HubConnectionBuilder 使用
C#WithAutomaticReconnect
方法来自动重新连接。 默认情况下,它不会自动重新连接。HubConnection connection= new HubConnectionBuilder() .WithUrl(new Uri("http://127.0.0.1:5000/chathub")) .WithAutomaticReconnect() .Build();
在没有任何参数的情况下,
WithAutomaticReconnect()
将客户端配置为在每次尝试重新连接之前分别等待 0、2、10 和 30 秒,在四次尝试失败后停止。在开始任何重新连接尝试之前,
C#HubConnection
会转换为HubConnectionState.Reconnecting
状态,并触发Reconnecting
事件。 这样就可警告用户连接已丢失,并提醒其禁用 UI 元素。 非交互式应用可以开始排队或删除消息。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; };
如果客户端在其前四次尝试中成功重新连接,则
HubConnection
将转换回Connected
状态并触发Reconnected
事件。 这样就可通知用户已重新建立连接并对任何已排队的消息取消排队。由于连接对服务器来说是全新的,因此将为
Reconnected
事件处理程序提供一个新的ConnectionId
。警告
如果
C#HubConnection
已配置为跳过协商,则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; };
C#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); } } }
如果客户端在其前四次尝试中未成功重新连接,则
C#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; };
若要在断开连接或更改重新连接时间安排之前配置自定义的重新连接尝试次数,
C#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 秒后启动,这与默认配置相同。
自定义行为再次与默认行为不同,它会在第三次重新连接尝试失败后停止。 在默认配置中,会在接下来 30 秒过后再次尝试重新连接。
如果想要更好地控制自动重新连接尝试的时间安排和次数,则
WithAutomaticReconnect
需接受实现IRetryPolicy
接口的对象,该对象具有一个名为NextRetryDelay
的方法。NextRetryDelay
采用一个RetryContext
类型的参数。RetryContext
具有三个属性:PreviousRetryCount
、ElapsedTime
和RetryReason
,分别为long
、TimeSpan
和Exception
。 在尝试第一次重新连接之前,PreviousRetryCount
和ElapsedTime
将为零,RetryReason
将是导致连接丢失的异常。 在每次重试失败后,PreviousRetryCount
的数量将增加一个,并更新ElapsedTime
以反映截止目前重新连接所花的时间,RetryReason
将是导致最后一个重新连接尝试失败的异常。
C#NextRetryDelay
必须返回表示在下次重新连接尝试之前等待的时间的 TimeSpan,或者在null
应停止重新连接的情况下返回HubConnection
。
C#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 事件可响应连接丢失。 例如,你可能要自动重新连接。
C#Closed
事件需要一个返回Task
的委托,这使异步代码可在不使用async void
的情况下运行。 若要在同步运行的Closed
事件处理程序中满足委托签名,请返回Task.CompletedTask
:connection.Closed += (error) => { // Do your close logic. return Task.CompletedTask; };
异步支持的主要原因是使你可以重启连接。 启动连接是一种异步操作。
在重启连接的
C#Closed
处理程序中,可考虑等待一些随机延迟以防止使服务器过载,如下面的示例中所示:connection.Closed += async (error) => { await Task.Delay(new Random().Next(0,5) * 1000); await connection.StartAsync(); };
从客户端调用中心方法
C#InvokeAsync
会对中心调用方法。 将中心方法中定义的中心方法名称和所有参数传递给InvokeAsync
。 SignalR 是异步的,因此在进行调用时请使用async
和await
。await connection.InvokeAsync("SendMessage", userTextBox.Text, messageTextBox.Text);
InvokeAsync
方法会返回一个在服务器方法返回时完成的Task
。 返回值(如果有)作为Task
的结果提供。 服务器上的方法所引发的任何异常都会产生出错的Task
。 使用await
语法等待服务器方法完成,并使用try...catch
语法处理错误。SendAsync
方法会返回一个在消息已发送到服务器时完成的Task
。 不会提供返回值,因为此Task
不会等到服务器方法完成。 发送消息期间在客户端上引发的任何异常都会产生出错的Task
。 使用await
和try...catch
语法处理发送错误。备注
仅在默认模式下使用 Azure SignalR 服务时,才支持从客户端调用中心方法。 有关详细信息,请参阅常见问题解答(azure-signalr GitHub 存储库)。
从中心调用客户端方法
在生成之后,但是在启动连接之前使用
C#connection.On
定义中心调用的方法。connection.On<string, string>("ReceiveMessage", (user, message) => { this.Dispatcher.Invoke(() => { var newMessage = $"{user}: {message}"; messagesList.Items.Add(newMessage); }); });
C#connection.On
中的上述代码在服务器端代码使用SendAsync
方法调用它时运行。public async Task SendMessage(string user, string message) { await Clients.All.SendAsync("ReceiveMessage", user,message); }
备注
虽然连接的中心端支持强类型消息,但客户端必须使用带有方法名称的泛型方法 HubConnection.On 进行注册。 有关示例,请参阅后台服务中的主机 ASP.NET CoreSignalR。
错误处理和日志记录
使用 try-catch 语句处理错误。 检查
C#Exception
对象,以确定发生错误后要执行的适当操作。try { await connection.InvokeAsync("SendMessage", userTextBox.Text, messageTextBox.Text); } catch (Exception ex) { messagesList.Items.Add(ex.Message); }
其他资源
建议的内容
-
将 SignalR 用户映射到连接
本主题演示如何保留有关用户及其连接的信息。 帕特里克·弗莱彻帮助撰写了本主题。 本主题中使用的软件版本...
-
在 SignalR
本主题介绍如何使用中心 API 保存组成员身份信息。
-
ASP.NET SignalR 中心 API 指南 - .NET 客户端 (C#)
本文档介绍在 .NET 客户端中使用适用于 SignalR 版本 2 的中心 API,例如 Windows 应用商店 (WinRT) 、WPF、Silverlight 和 cons...
-
ASP.NET SignalR 中心 API 指南 - 服务器 (C#)
本文档介绍了如何使用代码示例演示...,介绍 ASP.NET SignalR 中心 API for SignalR 版本 2 的服务器端编程。
-
管理 SignalR 中的用户和组
ASP.NET Core SignalR 用户和组管理概述。
-
SignalR 与 ASP.NET Core SignalR 的区别
SignalR 与 ASP.NET Core SignalR 的区别
-
SignalR 测试和调试
如何测试和调试 SignalR 应用程序。
-
ASP.NET Core SignalR JavaScript 客户端
ASP.NET Core SignalR JavaScript 客户端概述。