C# WebSocket游戏客户端退出时未完成关闭握手的问题排查与修复
问题原因与修复方案
老哥,这个问题其实很好理解:当你在控制台按"x"直接退出游戏时,客户端进程是强制终止的,根本没来得及给服务器发送WebSocket的Close帧完成握手流程。服务器这边的ReceiveAsync方法一直在等待客户端的消息,结果连接突然被切断,自然就抛出了那个"远程方未完成关闭握手就关闭了连接"的错误。
而你的现有代码里完全没有捕获这个异常,所以错误就直接暴露出来了。接下来给你两个层面的修复方案:
1. 服务器端添加异常捕获与连接清理
你需要在Receive方法里增加try-catch块,专门处理WebSocket连接被强制断开的异常,同时做好连接资源的清理工作。修改后的代码如下:
public async Task Receive(WebSocket socket) { var buffer = new byte[1024 * 4]; try { while (socket.State == WebSocketState.Open) { WebSocketReceiveResult result = await socket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None); if (result.MessageType == WebSocketMessageType.Text) { Console.WriteLine($"Receive->Text"); // 这里可以保留你原有的文本消息处理逻辑 } else if (result.MessageType == WebSocketMessageType.Close) { // 处理正常的关闭握手 string id = _sockets.FirstOrDefault(s => s.Value == socket).Key; _sockets.TryRemove(id, out _); Console.WriteLine("Managed Connections: " + _sockets.Count.ToString()); await socket.CloseOutputAsync(result.CloseStatus.Value, result.CloseStatusDescription, CancellationToken.None); return; } } } catch (WebSocketException ex) { // 捕获连接被强制断开的异常 if (ex.WebSocketErrorCode == WebSocketError.ConnectionClosedPrematurely) { Console.WriteLine("客户端强制断开了连接"); } else { // 其他WebSocket异常可以在这里记录日志 Console.WriteLine($"WebSocket异常: {ex.Message}"); } // 无论哪种异常,都要清理无效连接 var socketEntry = _sockets.FirstOrDefault(s => s.Value == socket); if (!string.IsNullOrEmpty(socketEntry.Key)) { _sockets.TryRemove(socketEntry.Key, out _); Console.WriteLine("Managed Connections: " + _sockets.Count.ToString()); } // 确保socket资源释放 if (socket.State != WebSocketState.Closed) { try { await socket.CloseAsync(WebSocketCloseStatus.AbnormalClosure, "连接异常断开", CancellationToken.None); } catch { /* 忽略关闭时的异常,避免二次报错 */ } } socket.Dispose(); } }
这段修改的关键点:
- 用
try-catch包裹整个接收循环,捕获WebSocketException - 针对
ConnectionClosedPrematurely错误做专门处理(就是你遇到的那种情况) - 无论异常类型,都从
_sockets中移除无效连接,避免连接管理器里残留垃圾连接 - 最后确保socket被正确关闭和释放,防止资源泄漏
2. 客户端添加主动关闭逻辑(可选但推荐)
如果能修改客户端代码,最好在用户按"x"退出时,先主动发起WebSocket关闭握手,再退出程序。比如在客户端的退出逻辑里加这段代码:
// 用户按x时的处理逻辑 if (Console.ReadKey().Key == ConsoleKey.X) { if (webSocket.State == WebSocketState.Open) { await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "用户主动退出", CancellationToken.None); } webSocket.Dispose(); Environment.Exit(0); }
这样客户端会先给服务器发送Close帧,服务器就能走正常的MessageType.Close分支处理,不会触发异常,体验更流畅。
内容的提问来源于stack exchange,提问作者Josh
相关产品推荐
相关产品推荐

