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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.08 13:52:54