退出登录后中止SignalR连接的实现方案
实现退出登录时中止用户所有SignalR连接
要实现退出时中止用户的所有SignalR连接,你需要先跟踪用户与连接ID的映射关系,再通过连接ID逐个中止连接。SignalR本身没有直接通过用户ID批量中止连接的API,所以需要自定义连接跟踪逻辑,具体步骤如下:
1. 创建连接跟踪服务
实现一个单例服务,用来存储用户ID和对应连接ID的集合:
using System.Collections.Concurrent; using System.Collections.Generic; public class ConnectionTrackingService { // 键:用户ID(ClaimTypes.NameIdentifier的值),值:该用户的所有连接ID集合 private readonly ConcurrentDictionary<string, HashSet<string>> _userConnections = new(); // 添加用户连接 public void AddConnection(string userId, string connectionId) { _userConnections.AddOrUpdate(userId, _ => new HashSet<string> { connectionId }, (_, connections) => { connections.Add(connectionId); return connections; }); } // 移除用户连接 public void RemoveConnection(string userId, string connectionId) { if (_userConnections.TryGetValue(userId, out var connections)) { connections.Remove(connectionId); // 如果用户没有剩余连接,清理条目 if (connections.Count == 0) { _userConnections.TryRemove(userId, out _); } } } // 获取用户的所有连接ID public IEnumerable<string> GetUserConnections(string userId) { _userConnections.TryGetValue(userId, out var connections); return connections ?? Enumerable.Empty<string>(); } }
2. 注册连接跟踪服务
在Program.cs(或Startup.cs)中将该服务注册为单例:
builder.Services.AddSingleton<ConnectionTrackingService>();
3. 修改SignalR Hub跟踪连接
在你的TestHub中注入连接跟踪服务,重写连接/断开方法来维护映射关系:
using Microsoft.AspNetCore.SignalR; using System.Security.Claims; public class TestHub : Hub { private readonly ConnectionTrackingService _connectionTrackingService; public TestHub(ConnectionTrackingService connectionTrackingService) { _connectionTrackingService = connectionTrackingService; } public override async Task OnConnectedAsync() { var userId = Context.User.FindFirst(ClaimTypes.NameIdentifier)?.Value; if (!string.IsNullOrEmpty(userId)) { _connectionTrackingService.AddConnection(userId, Context.ConnectionId); } await base.OnConnectedAsync(); } public override async Task OnDisconnectedAsync(Exception? exception) { var userId = Context.User.FindFirst(ClaimTypes.NameIdentifier)?.Value; if (!string.IsNullOrEmpty(userId)) { _connectionTrackingService.RemoveConnection(userId, Context.ConnectionId); } await base.OnDisconnectedAsync(exception); } }
4. 修改AuthController的SignOutAsync方法
注入ConnectionTrackingService,在发送退出通知后,遍历用户的所有连接ID并调用中止方法:
using Microsoft.AspNetCore.SignalR; using System.Security.Claims; [Route("api/[controller]")] [ApiController] public class AuthController : ControllerBase { // ... 其他代码保持不变 ... [HttpDelete("SignOut")] [Authorize] public async Task SignOutAsync(IHubContext<Hubs.TestHub> hubContext, ConnectionTrackingService connectionTrackingService) { if (hubContext != null) { var nameIdentifier = User?.FindFirst(ClaimTypes.NameIdentifier)?.Value; if (!string.IsNullOrWhiteSpace(nameIdentifier)) { await hubContext.Clients.User(nameIdentifier).SendAsync("TestHubNotify", $"Sign Out: {nameIdentifier}!"); // 中止该用户的所有SignalR连接 var connectionIds = connectionTrackingService.GetUserConnections(nameIdentifier); foreach (var connectionId in connectionIds) { await hubContext.AbortConnectionAsync(connectionId); } } } await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme); } }
关键说明
AbortConnectionAsync是SignalR提供的扩展方法,需要引用using Microsoft.AspNetCore.SignalR;命名空间。- 服务器端主动中止连接比仅通知客户端更可靠,能确保用户所有连接立即断开,避免客户端忽略通知的情况。
- 连接跟踪服务使用
ConcurrentDictionary确保多线程环境下的线程安全,适合SignalR的并发连接场景。
内容的提问来源于stack exchange,提问作者GranmaSquid3
相关产品推荐
相关产品推荐

