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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 22:08:09