.NET Core SignalR连接时消息拼接致connection.on不触发问题求助
SignalR连接时空消息与配置消息拼接导致客户端回调失效的问题
问题详情
我基于.NET Core开发的SignalR应用中,客户端连接时会收到包含应用配置的消息,但SignalR同时会额外发送一条payload为{}的空消息。偶尔(不同环境下概率在1/5到1/25之间),这条空消息会和配置消息被拼接成一条发送,导致客户端connection.on方法完全无法触发。通过Chrome开发者工具可以看到,拼接后的消息包含空消息{}、帧分隔符以及配置消息的SignalR信封,但服务端和客户端的SignalR均未抛出任何错误。
已尝试但无效的方案:
- 重构后端代码,使用连接ID定向发送消息替代
Clients.Caller - 在客户端添加自定义格式化器移除前置空消息,推测因SignalR不识别处理后的payload而静默忽略
- 考虑给配置消息添加100ms延迟,但这只是临时 workaround,希望找到根本解决办法
相关代码
Startup.cs
services.AddSignalR(); ... app.UseEndpoints( endpoints => { endpoints.MapDefaultControllerRoute(); endpoints.MapControllers(); endpoints.MapRazorPages(); endpoints.MapHub<MyHub>(Url.DefaultHub); });
服务端Hub实现
public sealed class MyHub(IAppSettingsChannel appSettingsChannel) : Hub { /// <summary> /// 用户连接WebSocket时触发的事件处理程序,每个用户连接都会调用 /// </summary> public override async Task OnConnectedAsync() { // 新用户连接或老用户重连时发送应用配置 await appSettingsChannel.Send(Clients.Caller); await base.OnConnectedAsync(); } }
服务端消息通道
public class AppSettingsChannel : IAppSettingsChannel { private static HubChannel Channel => HubChannel.AppSettings; internal AppSettingsModel AppSettings => new(...); public async Task Send(ISingleClientProxy caller) { await caller.SendAsync(Channel, AppSettings); } }
客户端Hub封装
export default class Hub { /** 底层WebSocket连接 */ private static connection: HubConnection | null = null; /** WebSocket连接的基础URL,不同Hub对应不同通道URL */ private static defaultHubUrl: string = '/web-socket'; /** 初始高频重试周期 */ private static initialRetryPeriodMilliseconds: number = 60 * 1000; /** 初始重试周期内的短间隔重试时长 */ private static initialRetryMilliseconds: number = 5 * 1000; /** 初始周期后的长间隔重试时长 */ private static continuousRetryMilliseconds: number = 30 * 1000; /** * 监听指定通道的消息,若未建立连接则先创建连接 * @param channel 要监听的服务端消息通道 * @param onMessage 收到消息时执行的回调函数 */ static async on<T>(channel: HubChannel, onMessage: (data: T) => void) { // 若未连接到服务端WebSocket,先建立连接 if (!this.connection) this.connection = await this.openConnection(); // 收到指定通道消息时执行回调 this.connection.on(channel.value, (msg: T) => { onMessage(msg); }); } /** * 建立与服务端WebSocket的连接 * @returns 已建立的连接 */ static async openConnection(): Promise<HubConnection> { // 创建WebSocket连接 this.connection = new HubConnectionBuilder() .withUrl(this.defaultHubUrl) .withAutomaticReconnect({ // 初始重试周期内用短间隔,之后用长间隔 nextRetryDelayInMilliseconds: (retryContext) => ( retryContext.elapsedMilliseconds < this.initialRetryPeriodMilliseconds ? this.initialRetryMilliseconds : this.continuousRetryMilliseconds ) }) .build(); // 连接关闭时若有错误则记录日志 this.connection.onclose(async (error) => { if (error) { console.error('Hub连接关闭并报错:', error); const apiError = normalizeError(error); await logErrorSafe(apiError); } }); // 启动连接 await this.connection.start(); return this.connection; } }
可行解决方案
1. 升级SignalR版本并调整消息发送时机
这个帧拼接问题大概率是特定版本的SignalR底层Bug,建议先升级到最新的.NET Core SignalR服务端包和JavaScript客户端包。同时调整OnConnectedAsync的执行顺序,先完成基类的连接初始化再发送消息:
public override async Task OnConnectedAsync() { await base.OnConnectedAsync(); // 等待连接完全建立后再发送配置消息 await appSettingsChannel.Send(Clients.Caller); }
2. 客户端全局监听所有消息,手动拆分拼接内容
通过connection.on('*')监听所有原始消息,识别并拆分拼接的JSON结构后手动触发对应通道的回调:
this.connection.on('*', (invocationData) => { const rawPayload = JSON.stringify(invocationData); // 检测是否存在JSON拼接的特征标记 if (rawPayload.includes('}{')) { // 拆分并修复每个JSON片段的格式 const splitSegments = rawPayload.split('}{').map((seg, index, arr) => { if (index === 0) return seg + '}'; if (index === arr.length - 1) return '{' + seg; return `{${seg}}`; }); splitSegments.forEach(seg => { try { const parsed = JSON.parse(seg); // 触发对应通道的已注册回调 const handlers = this.connection!.handlers[parsed.target]; handlers?.forEach(handler => handler(...parsed.arguments)); } catch (err) { console.error('处理拆分消息失败:', err); } }); } });
3. 启用SignalR的详细日志排查根因
在服务端开启SignalR的详细日志,追踪空消息的来源:
// 在Program.cs或Startup.cs中配置日志 builder.Logging.AddFilter("Microsoft.AspNetCore.SignalR", LogLevel.Debug); builder.Logging.AddFilter("Microsoft.AspNetCore.Http.Connections", LogLevel.Debug);
查看日志中是否有异常的连接初始化流程,确认空消息是SignalR框架自身发送还是业务代码的隐性触发。
内容的提问来源于stack exchange,提问作者Chris Harrington
相关产品推荐
相关产品推荐

