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

.NET Core中事件触发时通过WebSocket向客户端发送数据

解决.NET Core后台任务事件触发时向WebSocket客户端推送消息的问题

嘿,我完全懂这种卡在事件联动和WebSocket推送之间的困扰,咱们一步步把这个问题搞定。基于你已经配置了app.UseWebSockets()的前提,接下来需要完成几个核心步骤:

1. 管理已连接的WebSocket客户端

首先得有一个线程安全的集合来存储所有活跃的WebSocket连接——毕竟后台任务可能在任意线程触发事件,而WebSocket连接的添加/移除也会在不同请求线程里发生。推荐用ConcurrentBag<WebSocket>:

// 在Program.cs(.NET 6+)或者Startup.cs里定义静态集合
public static readonly ConcurrentBag<WebSocket> ActiveWebSockets = new();

2. 实现WebSocket连接端点

接下来需要一个端点来处理客户端的WebSocket连接请求,完成握手并维护连接:

// .NET 6+ 最小API写法
app.Map("/ws", async context =>
{
    if (!context.WebSockets.IsWebSocketRequest)
    {
        context.Response.StatusCode = StatusCodes.Status400BadRequest;
        return;
    }

    using var webSocket = await context.WebSockets.AcceptWebSocketAsync();
    ActiveWebSockets.Add(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);
    }
    catch (Exception ex)
    {
        // 可在此记录异常日志
        Console.WriteLine($"WebSocket error: {ex.Message}");
    }
    finally
    {
        ActiveWebSockets.TryTake(out _);
    }
});

3. 订阅后台任务的事件并推送消息

假设你的后台任务是一个注册为服务的类(比如BackgroundTaskService),你需要在合适的地方订阅它的三个事件,当事件触发时遍历活跃连接发送消息:

// 在Program.cs里获取后台任务服务实例并订阅事件
var backgroundTask = app.Services.GetRequiredService<BackgroundTaskService>();

// 定义通用的消息发送方法
async Task SendMessageToAllClients(string message)
{
    var messageBytes = Encoding.UTF8.GetBytes(message);
    var socketsToRemove = new List<WebSocket>();

    foreach (var socket in ActiveWebSockets)
    {
        if (socket.State != WebSocketState.Open)
        {
            socketsToRemove.Add(socket);
            continue;
        }

        try
        {
            await socket.SendAsync(
                new ArraySegment<byte>(messageBytes, 0, messageBytes.Length),
                WebSocketMessageType.Text,
                endOfMessage: true,
                CancellationToken.None);
        }
        catch (Exception)
        {
            socketsToRemove.Add(socket);
        }
    }

    // 移除无效连接
    foreach (var socket in socketsToRemove)
    {
        ActiveWebSockets.TryTake(out _);
    }
}

// 订阅三个事件
backgroundTask.OnStarted += async (sender, e) => await SendMessageToAllClients("event fired");
backgroundTask.OnCompleted += async (sender, e) => await SendMessageToAllClients("event fired");
backgroundTask.TaskProgress += async (sender, e) => await SendMessageToAllClients("event fired");

4. 关键注意事项

  • 线程安全:必须用线程安全集合,避免多线程操作时的竞态条件。
  • 连接状态检查:发送消息前一定要确认WebSocket状态为Open,否则会抛出异常。
  • 异常处理:发送消息时可能因客户端断开触发异常,要及时捕获并移除无效连接。
  • 后台任务注册:确保你的后台任务服务已正确注册(比如builder.Services.AddSingleton<BackgroundTaskService>()或实现IHostedService)。

测试验证

你可以用两种方式快速测试:

  • 浏览器控制台连接:
    const ws = new WebSocket('ws://localhost:5000/ws');
    ws.onmessage = (event) => console.log('Received:', event.data);
    
  • 命令行工具wscat:
    wscat -c ws://localhost:5000/ws
    

触发后台任务后,就能看到客户端收到"event fired"的消息了。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.22 08:00:54