Angular与ASP.NET Core SignalR单聊消息无法接收问题排查
Angular + ASP.NET Core SignalR 私密消息无法送达问题排查
我用Angular和ASP.NET Core SignalR实现用户间私密对话系统,SignalR连接与消息发送均显示成功,但消息无法送达目标用户。需求是仅通过用户ID在两人间发送消息,不使用群组。控制台显示发送方ID、接收方ID和消息内容均正确,但接收方无法收到消息,请问可能的原因是什么?
服务端ChatHub代码
using Microsoft.AspNetCore.SignalR; namespace FineMaster.Server.Hubs { public class ChatHub : Hub { public async Task SendPrivateMessage(int senderId, int receiverId, string message) { await Clients.Client(receiverId.ToString()).SendAsync("ReceiveMessage", senderId, message); } } }
客户端service.ts代码
import { Injectable } from '@angular/core'; import { HubConnection, HubConnectionBuilder } from '@aspnet/signalr'; import { Observable, Subject } from 'rxjs'; @Injectable({ providedIn: 'root' }) export class ChatService { private hubConnection: HubConnection; private message$: Subject<any> = new Subject(); constructor() { this.hubConnection = new HubConnectionBuilder() .withUrl('https://localhost:7235/chat') .build(); } async startConnection(): Promise<void> { try { await this.hubConnection.start(); console.log('SignalR connected!'); } catch (error) { console.error('Error connecting to SignalR:', error); } } sendMessage(senderId: number, receiverId: number, message: string): Promise<boolean> { return this.hubConnection.invoke('SendPrivateMessage', senderId, receiverId, message) .then(() => { console.log(senderId + 'says ' + message + " to " + receiverId); return true; // Indicate successful message sending }) .catch(error => { console.error('Error sending message:', error); return false; // Indicate error during message sending }); } onReceiveMessage(): Observable<string> { this.hubConnection.on("ReceiveMessage", (messages) => { this.message$.next(messages); }); return this.message$.asObservable(); } }
接收消息的组件代码
ngOnInit(): void { const storedUser = this.cookieService.get("userInfo"); if (storedUser) { var storedUserJson = JSON.parse(storedUser); this.currentUserID = storedUserJson.id; } this.chatService.startConnection(); this.chatService.onReceiveMessage().subscribe(response => { console.log("bişey geldi: ", response); }); }
问题原因及修复方案
1. 核心错误:混淆SignalR连接ID与业务用户ID
SignalR的Clients.Client()方法需要传入SignalR生成的连接ID,而非你业务系统中的用户ID。你直接将receiverId(业务用户ID)转成字符串当作连接ID使用,这完全不匹配——每个用户的SignalR连接ID是随机生成的,和业务用户ID没有关联。
修复方案:在服务端维护用户ID与连接ID的映射:
using Microsoft.AspNetCore.SignalR; using System.Collections.Concurrent; namespace FineMaster.Server.Hubs { public class ChatHub : Hub { // 存储用户ID对应的所有连接ID(支持多端登录) private static readonly ConcurrentDictionary<int, HashSet<string>> _userConnections = new(); public override async Task OnConnectedAsync() { // 从认证信息中获取当前用户ID(根据你的认证方式调整,比如JWT/Cookie) if (Context.User.Identity?.IsAuthenticated == true) { var userId = int.Parse(Context.User.FindFirst("userId")?.Value!); var connectionId = Context.ConnectionId; _userConnections.AddOrUpdate(userId, new HashSet<string> { connectionId }, (key, existing) => { existing.Add(connectionId); return existing; }); } await base.OnConnectedAsync(); } public override async Task OnDisconnectedAsync(Exception? exception) { if (Context.User.Identity?.IsAuthenticated == true) { var userId = int.Parse(Context.User.FindFirst("userId")?.Value!); var connectionId = Context.ConnectionId; if (_userConnections.TryGetValue(userId, out var connections)) { connections.Remove(connectionId); if (connections.Count == 0) { _userConnections.TryRemove(userId, out _); } } } await base.OnDisconnectedAsync(exception); } public async Task SendPrivateMessage(int senderId, int receiverId, string message) { // 根据用户ID获取对应的所有连接ID,逐个发送消息 if (_userConnections.TryGetValue(receiverId, out var connections)) { foreach (var connectionId in connections) { await Clients.Client(connectionId).SendAsync("ReceiveMessage", senderId, message); } } } } }
2. 客户端消息接收参数不匹配
服务端调用SendAsync("ReceiveMessage", senderId, message)传入了两个参数(发送方ID、消息内容),但客户端on("ReceiveMessage", (messages) => { ... })只接收了一个参数,导致无法正确解析数据。
修复方案:调整客户端监听逻辑,匹配参数数量:
onReceiveMessage(): Observable<{ senderId: number; message: string }> { // 仅注册一次监听,避免重复订阅 if (!this.hubConnection._handlers.has("ReceiveMessage")) { this.hubConnection.on("ReceiveMessage", (senderId: number, message: string) => { this.message$.next({ senderId, message }); }); } return this.message$.asObservable(); }
3. 潜在问题:重复注册消息监听
每次调用onReceiveMessage()都会重新注册ReceiveMessage事件,多次触发会导致重复订阅,引发异常或数据混乱。上面的修复方案已经通过判断_handlers.has("ReceiveMessage")避免了这个问题。
内容的提问来源于stack exchange,提问作者Mustafa Ucar
相关产品推荐
相关产品推荐

