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

如何在.NET中使用WebSocket向指定用户发送聊天消息?

在.NET中基于UserId实现WebSocket实时消息推送

HTTP方案需要刷新页面才能获取新消息,本质是因为HTTP是请求-响应模式,无法主动向客户端推送数据。WebSocket是双向实时通信协议,能完美解决这个问题。下面分原生WebSocket和**SignalR(WebSocket封装框架)**两种方案实现:


一、原生WebSocket实现

1. 配置WebSocket中间件

在Program.cs中启用WebSocket支持,并注册全局连接字典(线程安全,用于维护用户ID与WebSocket连接的映射):

var builder = WebApplication.CreateBuilder(args);

// 注册线程安全的连接字典
builder.Services.AddSingleton<ConcurrentDictionary<string, WebSocket>>();

var app = builder.Build();

// 启用WebSocket中间件
app.UseWebSockets();

// 其他中间件配置(如认证、路由等)

2. 创建WebSocket连接端点

添加一个端点处理客户端连接请求,将用户ID与WebSocket关联:

app.Map("/ws/{userId}", async context =>
{
    if (!context.WebSockets.IsWebSocketRequest)
    {
        context.Response.StatusCode = StatusCodes.Status400BadRequest;
        return;
    }

    var userId = context.Request.RouteValues["userId"].ToString();
    using var webSocket = await context.WebSockets.AcceptWebSocketAsync();
    
    var connections = app.Services.GetRequiredService<ConcurrentDictionary<string, WebSocket>>();
    connections.TryAdd(userId, webSocket);

    try
    {
        var buffer = new byte[1024 * 4];
        WebSocketReceiveResult result;
        // 保持连接监听客户端消息(无需处理可忽略)
        do
        {
            result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
        } while (!result.CloseStatus.HasValue);

        await webSocket.CloseAsync(result.CloseStatus.Value, result.CloseStatusDescription, CancellationToken.None);
    }
    finally
    {
        // 连接关闭时从字典移除
        connections.TryRemove(userId, out _);
    }
});

app.Run();

3. 实现定向消息推送

创建消息服务,根据用户ID找到对应连接并发送消息:

public class MessageSenderService
{
    private readonly ConcurrentDictionary<string, WebSocket> _connections;

    public MessageSenderService(ConcurrentDictionary<string, WebSocket> connections)
    {
        _connections = connections;
    }

    public async Task SendToUser(string userId, string messageContent)
    {
        if (_connections.TryGetValue(userId, out var webSocket) && webSocket.State == WebSocketState.Open)
        {
            var messageBytes = Encoding.UTF8.GetBytes(messageContent);
            await webSocket.SendAsync(
                new ArraySegment<byte>(messageBytes),
                WebSocketMessageType.Text,
                endOfMessage: true,
                CancellationToken.None
            );
        }
        else
        {
            // 用户未在线,可将消息存入数据库后续重试
        }
    }
}

4. 前端连接与接收消息

用JavaScript建立WebSocket连接,实时接收消息并更新页面:

const currentUserId = "your-user-id"; // 替换为当前用户ID
const socket = new WebSocket(`ws://localhost:5000/ws/${currentUserId}`);

socket.onopen = () => console.log("WebSocket连接成功");

socket.onmessage = (event) => {
    // 收到消息后直接更新页面,无需刷新
    const messageElement = document.createElement("div");
    messageElement.textContent = event.data;
    document.getElementById("message-container").appendChild(messageElement);
};

socket.onclose = () => console.log("WebSocket连接关闭");

二、SignalR实现(推荐,封装更完善)

SignalR是.NET官方的实时通信框架,基于WebSocket(自动降级到其他协议),简化了连接管理、消息路由等复杂逻辑。

1. 安装依赖

在项目中安装SignalR包:

Install-Package Microsoft.AspNetCore.SignalR

2. 创建消息Hub类

Hub是SignalR的核心组件,负责处理客户端连接和消息路由:

public class UserMessageHub : Hub
{
    // 客户端连接时,将连接ID加入对应用户的分组
    public override async Task OnConnectedAsync()
    {
        // 从认证上下文获取用户ID(需配置认证)
        var userId = Context.User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
        if (!string.IsNullOrEmpty(userId))
        {
            await Groups.AddToGroupAsync(Context.ConnectionId, userId);
        }
        await base.OnConnectedAsync();
    }

    // 客户端断开时移除分组
    public override async Task OnDisconnectedAsync(Exception? exception)
    {
        var userId = Context.User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
        if (!string.IsNullOrEmpty(userId))
        {
            await Groups.RemoveFromGroupAsync(Context.ConnectionId, userId);
        }
        await base.OnDisconnectedAsync(exception);
    }
}

3. 配置SignalR中间件

在Program.cs中注册并映射Hub:

var builder = WebApplication.CreateBuilder(args);

// 注册SignalR服务
builder.Services.AddSignalR();
// 配置认证(可选,用于获取用户ID)
builder.Services.AddAuthentication();

var app = builder.Build();

// 映射Hub端点
app.MapHub<UserMessageHub>("/userMessageHub");

app.Run();

4. 定向推送消息

在任意服务中注入IHubContext,即可向指定用户推送消息:

public class NotificationService
{
    private readonly IHubContext<UserMessageHub> _hubContext;

    public NotificationService(IHubContext<UserMessageHub> hubContext)
    {
        _hubContext = hubContext;
    }

    public async Task SendNotification(string userId, string content)
    {
        // 向用户分组发送消息
        await _hubContext.Clients.Group(userId).SendAsync("ReceiveNotification", content);
    }
}

5. 前端接收消息

安装SignalR客户端包,建立连接并监听消息:

npm install @microsoft/signalr
import * as signalR from "@microsoft/signalr";

const connection = new signalR.HubConnectionBuilder()
    .withUrl("/userMessageHub")
    .build();

// 启动连接
connection.start().catch(err => console.error(err));

// 监听服务器推送的消息
connection.on("ReceiveNotification", (content) => {
    // 实时更新页面
    alert(`收到消息:${content}`);
});

关键注意事项

  • 线程安全:原生方案中必须用ConcurrentDictionary维护连接,避免多线程冲突。
  • 离线消息处理:若用户未在线,建议将消息存入数据库,待用户上线后再推送。
  • 认证授权:需确保只有合法用户能建立连接,可通过JWT、Cookie等方式验证用户ID。
  • 连接异常处理:捕获WebSocket/SignalR的连接断开事件,及时清理无效连接。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 21:36:05