1. 为什么WebSocket是聊天服务的终极方案十年前我刚入行时做实时通讯只能用Ajax轮询。记得当时每秒钟要发几十次请求服务器CPU直接飙到90%前端同事的浏览器内存泄漏到崩溃。直到遇到WebSocket才明白什么叫技术解放生产力。传统轮询就像你每隔5分钟打电话问女朋友你到哪了而WebSocket是打开微信共享实时位置。前者不仅费时费力80%的通信都是无效的你还在吗后者建立连接后所有数据流动都是有效信息。来看组实测数据对比指标轮询方案WebSocket延迟1-5秒50-300毫秒带宽占用高(含HTTP头)极低(无头)服务器负载高(频繁连接)低(长连接)移动端耗电严重轻微2. 五分钟快速搭建WebSocket服务用VS2022新建ASP.NET Core空项目NuGet添加Microsoft.AspNetCore.WebSockets。核心代码其实就三部分// Program.cs var builder WebApplication.CreateBuilder(args); builder.WebHost.UseUrls(http://*:8080); // 开放所有IP访问 var app builder.Build(); app.UseWebSockets(); // 启用WebSocket中间件 app.Map(/chat, async context { if (context.WebSockets.IsWebSocketRequest) { using var ws await context.WebSockets.AcceptWebSocketAsync(); while (true) { var buffer new byte[1024]; var result await ws.ReceiveAsync(buffer, CancellationToken.None); if (result.MessageType WebSocketMessageType.Close) break; // 这里处理消息... } } else context.Response.StatusCode 400; }); app.Run();测试时可以用Chrome控制台const ws new WebSocket(ws://localhost:8080/chat); ws.onmessage e console.log(e.data);3. 生产级连接管理实战实际项目中我最常遇到的坑是连接中断处理。分享个带心跳检测的增强版// 连接池管理 class WebSocketPool { private readonly ConcurrentDictionarystring, WebSocket _sockets new(); public void Add(string id, WebSocket ws) { _sockets.TryAdd(id, ws); _ StartHeartbeat(id); // 启动心跳任务 } private async Task StartHeartbeat(string id) { while (_sockets.TryGetValue(id, out var ws)) { try { await ws.SendAsync(/* ping帧 */); await Task.Delay(30000); // 30秒一次 } catch { Remove(id); } } } } // 在接收循环中加入 if (DateTime.Now - lastActive TimeSpan.FromMinutes(1)) { await ws.CloseAsync(WebSocketCloseStatus.NormalClosure, Timeout, CancellationToken.None); }4. 高性能消息广播技巧单机环境下用ConcurrentBag存储连接就能实现广播。但在K8s集群中需要结合Redis发布订阅// 消息广播服务 class BroadcastService { private readonly IConnectionMultiplexer _redis; public BroadcastService(IConnectionMultiplexer redis) { _redis redis; _redis.GetSubscriber().Subscribe(chat, (_, msg) { foreach (var ws in _activeConnections) ws.SendAsync(msg); // 实际需处理发送队列 }); } public async Task PublishAsync(string message) { await _redis.GetSubscriber().PublishAsync(chat, message); } }实测在4核8G的Linux主机上这个方案能支撑2万并发连接消息延迟控制在200ms内。5. 异常处理中的血泪教训去年我们线上服务出现过三次严重事故总结出这些必做防护连接泄露一定要用using包裹WebSocket对象或者确保CloseAsync被调用消息风暴客户端每秒发送超过100条消息时启用限流策略内存泄漏定期检查WebSocket.State移除Aborted状态的连接跨域问题生产环境一定要配置WithOriginsapp.UseCors(builder builder .WithOrigins(https://yourdomain.com) .AllowAnyHeader() .AllowAnyMethod());6. 进阶二进制消息处理除了文本聊天我们还能传输文件。关键是要正确处理消息分片var buffer new ArraySegmentbyte(new byte[8192]); using var ms new MemoryStream(); WebSocketReceiveResult result; do { result await ws.ReceiveAsync(buffer, CancellationToken.None); ms.Write(buffer.Array, buffer.Offset, result.Count); } while (!result.EndOfMessage); if (result.MessageType WebSocketMessageType.Binary) { File.WriteAllBytes(received.jpg, ms.ToArray()); }7. 与前端联调的那些坑最近帮团队解决过一个诡异问题Chrome正常但Safari频繁断开。最终发现是Safari对心跳响应要求更严格。解决方案// 前端增加异常处理 ws.onclose () { console.log(连接断开5秒后重连...); setTimeout(connect, 5000); };建议所有生产项目都要添加自动重连机制实现消息ACK确认加入消息序列号防重放8. 性能优化实战记录在压力测试时我们发现几个关键优化点缓冲区复用避免每次接收都new数组private readonly ArraySegmentbyte _sharedBuffer new(new byte[8192]);发送队列防止并发发送导致帧乱序var semaphore new SemaphoreSlim(1, 1); await semaphore.WaitAsync(); try { await ws.SendAsync(...); } finally { semaphore.Release(); }IO优化对于群聊场景改用PipeWriter提升吞吐量经过调优后单机消息吞吐量从800QPS提升到12K QPS效果立竿见影。