ASP.NET Core WebSocket如何检测客户端网络连接中断?
如何在ASP.NET Core WebSockets中快速检测客户端网络断开
针对你遇到的服务器要20分钟才感知客户端断连的问题,这里有几个实用的解决办法:
一、实现应用层自定义心跳机制
既然ManagedWebSocket内置的Ping/Pong没法直接追踪,那就自己在业务层加心跳:
- 客户端每隔固定时间(比如10秒)发送一条自定义心跳消息(比如
{"cmd":"heartbeat"}) - 服务器端维护一个字典,记录每个WebSocket连接的最后活跃时间
- 启动一个后台定时任务(比如用
IHostedService),每隔一段时间遍历字典,把超过超时阈值(比如30秒)的连接主动关闭
示例代码片段:
// 服务器端维护连接状态 private static readonly ConcurrentDictionary<WebSocket, DateTime> _activeConnections = new(); // 接收消息时更新活跃时间 async Task HandleWebSocketConnection(WebSocket webSocket) { _activeConnections.TryAdd(webSocket, DateTime.UtcNow); var buffer = new byte[1024 * 4]; try { while (webSocket.State == WebSocketState.Open) { var result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None); // 更新活跃时间 _activeConnections[webSocket] = DateTime.UtcNow; if (result.MessageType == WebSocketMessageType.Text) { var message = Encoding.UTF8.GetString(buffer, 0, result.Count); // 处理心跳消息(可选,也可以只更新时间不处理) if (message.Contains("heartbeat")) { await webSocket.SendAsync(Encoding.UTF8.GetBytes("pong"), WebSocketMessageType.Text, true, CancellationToken.None); } } } } catch (WebSocketException) { // 连接异常断开,清理 _activeConnections.TryRemove(webSocket, out _); } } // 后台清理任务 public class ConnectionCleanupService : IHostedService, IDisposable { private Timer _timer; public Task StartAsync(CancellationToken cancellationToken) { _timer = new Timer(CheckInactiveConnections, null, TimeSpan.Zero, TimeSpan.FromSeconds(15)); return Task.CompletedTask; } private void CheckInactiveConnections(object state) { var now = DateTime.UtcNow; var inactiveConnections = _activeConnections.Where(kv => now - kv.Value > TimeSpan.FromSeconds(30)).Select(kv => kv.Key).ToList(); foreach (var conn in inactiveConnections) { if (conn.State == WebSocketState.Open) { conn.CloseAsync(WebSocketCloseStatus.NormalClosure, "Inactive connection", CancellationToken.None).Wait(); } _activeConnections.TryRemove(conn, out _); } } public Task StopAsync(CancellationToken cancellationToken) { _timer?.Change(Timeout.Infinite, 0); return Task.CompletedTask; } public void Dispose() { _timer?.Dispose(); } }
二、配置TCP层面的KeepAlive
WebSocket基于TCP协议,可以通过配置Kestrel的TCP KeepAlive参数来缩短断连检测时间:
在Program.cs中添加Kestrel配置:
builder.WebHost.ConfigureKestrel(options => { options.ListenAnyIP(5000, listenOptions => { listenOptions.UseConnectionLogging(); listenOptions.TcpKeepAlive.Time = TimeSpan.FromSeconds(10); listenOptions.TcpKeepAlive.IdleTime = TimeSpan.FromSeconds(10); listenOptions.TcpKeepAlive.Interval = TimeSpan.FromSeconds(5); }); });
这样TCP层会主动发送KeepAlive包,一旦客户端断连,服务器能更快收到TCP层面的断开通知。
三、优化接收循环的异常处理
即使不用心跳,在服务器的消息接收循环里,一旦连接异常断开,ReceiveAsync会抛出WebSocketException或者返回带有CloseStatus的结果,你可以在循环里捕获这些情况,及时清理连接:
async Task HandleWebSocketConnection(WebSocket webSocket) { var buffer = new byte[1024 * 4]; try { while (webSocket.State == WebSocketState.Open) { var result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None); if (result.MessageType == WebSocketMessageType.Close) { await webSocket.CloseAsync(result.CloseStatus.Value, result.CloseStatusDescription, CancellationToken.None); break; } // 处理业务消息 } } catch (WebSocketException ex) { // 连接异常断开,这里可以做日志记录、清理连接等操作 Console.WriteLine($"Connection closed unexpectedly: {ex.Message}"); } finally { webSocket.Dispose(); } }
内容的提问来源于stack exchange,提问作者daniil_
相关产品推荐
相关产品推荐

