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

.NET Core 3.1 SignalR Hub运行20分钟后连接中断重连无数据

问题场景

环境为.NET Core 3.1,基于Kestrel将SignalR Hub作为Windows服务运行,客户端为v5.0.9版本的JavaScript SignalR客户端。可稳定复现运行20分钟后连接无理由中断,2秒后自动重连,但重连后的新连接无法发送新数据,未修改任何默认超时配置。


相关代码

服务端Startup.cs配置

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddCors();
        services.AddSignalR(hubOptions =>
        {
            hubOptions.EnableDetailedErrors = true;
            hubOptions.MaximumReceiveMessageSize = 1048576; //1MB
        }).AddJsonProtocol(options =>
        {
            options.PayloadSerializerOptions = ServiceTools.GetSerializerOptions();
            options.PayloadSerializerOptions.PropertyNameCaseInsensitive = true;
        });
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        app.UseCors(builder => builder
            .AllowAnyHeader()
            .AllowAnyMethod()
            .SetIsOriginAllowed(_ => true)
            .AllowCredentials()
        );

        app.UseRouting();
        app.UseEndpoints(endpoints =>
        {
            endpoints.MapHub<VisuNotificationHub>("/signalr");
        });
    }
}

客户端JS连接配置

pfw2.connection = new signalR.HubConnectionBuilder()
    .withUrl(signalrUrl,  ['webSockets', 'serverSentEvents', 'longPolling'])
    .configureLogging(signalR.LogLevel.Debug)
    .withAutomaticReconnect({
        nextRetryDelayInMilliseconds: retryContext => {
            if (retryContext.elapsedMilliseconds < 10000) {             //<10秒
                return 1000;                                            //每秒重试
            }
            else if (retryContext.elapsedMilliseconds < 60000) {        //<60秒/1分钟
                return 10000;                                           //每10秒重试
            }
            else if (retryContext.elapsedMilliseconds < 1800000) {      //<30分钟
                return 300000;                                          //每5分钟重试
            }
            else {                                                      //>= 30分钟
                return 900000;                                          //每15分钟重试
            }
        }
    })
    .build();

pfw2.connection.serverTimeoutInMilliseconds = 60000;

//重连中回调
pfw2.connection.onreconnecting(error => {
    var reconnectErrorData = [{ Message: `<p style="background:red;color:white;font-size:24px">@ResOperatorDialog.ErrorPleaseRefresh</p>` }];
    console.assert(connection.state === signalR.HubConnectionState.Reconnecting);
    console.log("Hub发生错误!", error);
    UpdateTicker(reconnectErrorData);
});

//重连成功回调
pfw2.connection.onreconnected((connectionId) => {
    onHubConnectionDone();
    console.log(connectionId);
});

//连接关闭回调
pfw2.connection.onclose((e) => {
    var connectioNClosedErrorData = [{Message: `<p style="background:red;color:white">连接已关闭,请刷新页面重试。</p>`}];
    UpdateTicker(connectioNClosedErrorData);
    console.log(e);
});   

//启动连接
pfw2.connection.start()
.catch((err) => {
    return console.error(err.toString());
})
.then(() => {
    console.log("连接成功,连接ID=",pfw2.connection.connectionId);
    onHubConnectionDone();
});

解决方案

按优先级依次排查修改:

1. 对齐超时配置,避免无意义断连

20分钟断连大概率是Kestrel默认的空闲连接超时、SignalR默认的客户端超时与客户端配置不匹配导致,修改两处配置:

  • 服务端SignalR配置补充超时、心跳设置,和客户端60秒超时匹配:
services.AddSignalR(hubOptions =>
{
    hubOptions.EnableDetailedErrors = true;
    hubOptions.MaximumReceiveMessageSize = 1048576; //1MB
    // 新增配置
    hubOptions.ClientTimeoutInterval = TimeSpan.FromSeconds(120); // 设置为心跳间隔的2倍即可
    hubOptions.KeepAliveInterval = TimeSpan.FromSeconds(30); // 服务端每30秒发送心跳包
});
  • 服务端Kestrel配置延长空闲连接超时,避免操作系统/Kestrel主动切断长期空闲的连接,在Program.cs的WebHost配置中添加:
webBuilder.ConfigureKestrel(options =>
{
    options.Limits.IdleTimeout = TimeSpan.FromHours(2); // 可按需调整时长
});

2. 修复重连后无法发数据的问题

  • 检查onHubConnectionDone方法逻辑:确认所有客户端监听服务端事件的注册代码、身份验证逻辑都在该方法内实现,不要只写在首次start()的成功回调里,重连后需要重新完成所有注册、鉴权操作才能正常收发数据。
  • 检查自定义连接映射/分组逻辑:如果你的服务端自己维护了ConnectionId和用户/业务的映射关系,重连后生成的新ConnectionId需要重新加入映射表、重新加入对应的分组,否则服务端推送消息还是会发送到已失效的旧连接,看起来就是新连接无法收发数据。
  • 强制指定WebSocket传输:修改客户端withUrl参数,暂时关闭长轮询、ServerSentEvents,排除传输协议切换导致的异常:
.withUrl(signalrUrl, signalR.HttpTransportType.WebSockets)

3. 排查版本兼容问题

服务端为.NET Core 3.1,客户端为SignalR 5.0版本,跨大版本可能存在序列化兼容问题,确认ServiceTools.GetSerializerOptions()返回的序列化配置和客户端默认序列化规则完全对齐,比如字段名大小写、空值处理、日期格式等。


内容的提问来源于stack exchange,提问作者DeckardCain2020

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.04 23:51:01