.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
相关产品推荐
相关产品推荐

