如何从SignalR服务端主动关闭半开WebSocket连接?
服务端主动关闭SignalR半开WebSocket连接方案
完全可以通过服务端主动关闭这类无响应的半开连接,结合你已实现的ping/pong机制,按以下步骤落地:
核心思路
通过维护每个连接的最后活跃时间,定时检查超时连接并调用SignalR的断开API强制关闭,同时清理服务端的连接状态记录。
具体实现步骤
1. 在Hub中维护连接心跳状态
用线程安全字典记录每个connectionId的最后一次pong响应时间:
using System.Collections.Concurrent; using Microsoft.AspNetCore.SignalR; public class YourHub : Hub { // 存储连接ID与最后活跃时间的映射 public readonly ConcurrentDictionary<string, DateTime> ConnectionLastActive = new(); public override async Task OnConnectedAsync() { ConnectionLastActive.TryAdd(Context.ConnectionId, DateTime.UtcNow); await base.OnConnectedAsync(); } // 客户端收到ping后调用此方法返回pong,更新活跃时间 public Task SendPong() { ConnectionLastActive.TryUpdate(Context.ConnectionId, DateTime.UtcNow, DateTime.MinValue); return Task.CompletedTask; } public override async Task OnDisconnectedAsync(Exception? exception) { ConnectionLastActive.TryRemove(Context.ConnectionId, out _); await base.OnDisconnectedAsync(exception); } }
2. 实现后台定时清理服务
创建一个后台服务,定期扫描超时连接并主动断开:
using Microsoft.AspNetCore.SignalR; using Microsoft.Extensions.Hosting; using System.Threading; using System.Threading.Tasks; public class ConnectionCleanupService : BackgroundService { private readonly IHubContext<YourHub> _hubContext; private readonly YourHub _yourHub; // 自定义超时时间(30秒) private readonly TimeSpan _inactiveTimeout = TimeSpan.FromSeconds(30); // 检查间隔(10秒) private readonly TimeSpan _checkInterval = TimeSpan.FromSeconds(10); public ConnectionCleanupService(IHubContext<YourHub> hubContext, YourHub yourHub) { _hubContext = hubContext; _yourHub = yourHub; } protected override async Task ExecuteAsync(CancellationToken stoppingToken) { while (!stoppingToken.IsCancellationRequested) { var currentTime = DateTime.UtcNow; // 筛选出超时的连接 var staleConnections = _yourHub.ConnectionLastActive .Where(kv => currentTime - kv.Value > _inactiveTimeout) .ToList(); foreach (var (connectionId, _) in staleConnections) { try { // 主动断开连接 await _hubContext.Clients.Client(connectionId).Abort(); // 从字典中移除记录 _yourHub.ConnectionLastActive.TryRemove(connectionId, out _); } catch (Exception ex) { // 此处添加日志记录,处理断开失败的异常 } } await Task.Delay(_checkInterval, stoppingToken); } } }
3. 注册后台服务
在Program.cs中注册这个后台清理服务:
builder.Services.AddHostedService<ConnectionCleanupService>(); // 确保Hub以单例或合适的生命周期注册(根据你的实际情况调整) builder.Services.AddSingleton<YourHub>();
注意事项
- 自定义的30秒超时建议比SignalR内置的WebSocket KeepAliveInterval更短,避免冲突。可以通过
AddSignalR配置调整内置超时:builder.Services.AddSignalR(options => { options.KeepAliveInterval = TimeSpan.FromSeconds(45); }); - 调用
Abort()后,对应连接的OnDisconnectedAsync方法会被触发,可在此完成后续的业务清理逻辑。
内容的提问来源于stack exchange,提问作者Aleksander Ciurej
相关产品推荐
相关产品推荐

