SignalR Core:从WebAPI检查SignalR客户端连接状态
检查SignalR客户端连接状态并避免重复启动
针对你跨服务器连接SignalR Hub的场景,这里有一套可靠的方案来确保连接只在需要时启动,同时处理并发和重连问题:
首先,HubConnection本身提供了State属性(类型为HubConnectionState枚举),我们可以通过它判断当前连接的状态,避免重复调用StartAsync(重复调用会抛出异常)。结合线程安全控制和自动重连逻辑,下面是修改后的完整代码示例:
using Microsoft.AspNetCore.SignalR.Client; using System.Threading; using System.Threading.Tasks; public class ChatApi { private readonly HubConnection _connection; // 用SemaphoreSlim确保多线程下不会并发启动连接 private readonly SemaphoreSlim _connectionLock = new SemaphoreSlim(1, 1); public ChatApi() { _connection = new HubConnectionBuilder() .WithUrl("https://your-signalr-hub-domain/chatHub") // 替换为你的Hub实际地址 // 可选:添加日志、指定传输方式等配置 .Build(); // 注册连接断开后的自动重连逻辑 _connection.Closed += async (error) => { // 随机延迟避免所有客户端同时重连导致服务器压力过大 await Task.Delay(new Random().Next(1000, 5000)); await EnsureConnectedAsync(); }; } /// <summary> /// 确保SignalR连接处于可用状态,避免重复启动 /// </summary> public async Task EnsureConnectedAsync() { await _connectionLock.WaitAsync(); try { // 只有当连接处于Disconnected状态时,才尝试启动 if (_connection.State == HubConnectionState.Disconnected) { await _connection.StartAsync(); // 这里可以添加日志记录连接成功 } // 如果是Connecting或Connected状态,直接跳过 } catch (Exception ex) { // 处理连接失败的情况,比如日志记录、告警等 // 可以根据需求添加重试逻辑,但注意不要无限重试 } finally { _connectionLock.Release(); } } /// <summary> /// 示例:调用Hub中的方法,先确保连接已建立 /// </summary> public async Task SendChatMessageAsync(string username, string message) { // 先确保连接可用 await EnsureConnectedAsync(); // 调用Hub端的方法(这里假设Hub有个SendMessage方法) await _connection.InvokeAsync("SendMessage", username, message); } }
关键细节说明:
- 状态判断:
HubConnectionState包含Disconnected、Connecting、Connected、Disconnecting四种状态,我们只需要在Disconnected时调用StartAsync,避免重复操作。 - 线程安全:因为WebAPI是多请求并发的环境,用
SemaphoreSlim做互斥锁,防止多个请求同时触发连接启动。 - 自动重连:通过
Closed事件注册重连逻辑,当连接意外断开时,自动尝试重新连接,提升可靠性。 - 跨服务器CORS配置:别忘了在你的SignalR Hub项目中配置CORS,允许WebAPI的域名访问,示例配置如下:
// Hub项目的Program.cs builder.Services.AddCors(options => { options.AddPolicy("AllowWebApiAccess", policy => { policy.WithOrigins("https://your-webapi-domain") // 替换为WebAPI的地址 .AllowAnyHeader() .AllowAnyMethod() .AllowCredentials(); // SignalR必须开启这个选项 }); }); // 在app.UseRouting()之后添加 app.UseCors("AllowWebApiAccess");
依赖注入建议
如果你的WebAPI使用依赖注入,建议将ChatApi注册为单例,因为每个HubConnection对应一个长连接,单例可以避免创建过多连接浪费资源:
// WebAPI项目的Program.cs builder.Services.AddSingleton<ChatApi>();
这样,你在调用Hub方法前先调用EnsureConnectedAsync,就能确保连接始终处于可用状态,同时不会重复启动连接了。
内容的提问来源于stack exchange,提问作者garethb
相关产品推荐
相关产品推荐

