You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何从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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.24 10:06:19