.NET 6中ClientWebSocket.ReceiveAsync首次返回后无响应问题
问题原因
- 模型适配错误:Godot的
WebSocketPeer是主动轮询模型,需要手动调用poll()检查状态和数据;但.NET的ClientWebSocket是异步被动模型,ReceiveAsync会自动异步等待直到有数据或连接状态变化,原代码强行套入轮询逻辑,用Thread.Sleep浪费资源且破坏异步流程。 - 异步方法误用:
async void类型的方法无法捕获异步异常,也无法被等待,一旦出现异常会直接导致状态混乱或程序崩溃。 - 接收逻辑不完整:单次循环只调用一次
ReceiveAsync,无法处理连续到达的多个数据包,Thread.Sleep的延迟还可能触发远程服务的超时关闭。 - 关闭处理缺失:远程关闭连接时,
ReceiveAsync会抛出异常,原代码仅在WebSocketState.Closed分支处理,未覆盖异常场景下的关闭逻辑,导致连接状态无法正确更新。
解决方案
修正思路是贴合.NET异步模型,放弃主动轮询,改用异步循环接收数据,完善异常和关闭逻辑:
修正后的接收循环代码
// 改用async Task作为返回类型,便于异常捕获和生命周期管理 private async Task ReceiveLoopAsync(CancellationToken cancellationToken) { try { // 复用缓冲区,避免每次循环创建新数组 var buffer = new byte[1024]; while (!cancellationToken.IsCancellationRequested && web_socket.State == WebSocketState.Open) { if (!socket_connected) { socket_connected = true; // 这里可以添加连接建立的通知逻辑(如触发自定义事件) } var memory = new Memory<byte>(buffer); Console.WriteLine("等待接收数据..."); var result = await web_socket.ReceiveAsync(memory, cancellationToken); Console.WriteLine("数据接收完成"); // 处理服务端主动发起的关闭请求 if (result.MessageType == WebSocketMessageType.Close) { await web_socket.CloseAsync(WebSocketCloseStatus.NormalClosure, "客户端响应关闭", cancellationToken); connection_closed(web_socket.CloseStatus); break; } if (result.Count > 0) { deserialize_event(memory.Slice(0, result.Count).ToArray()); } } } catch (WebSocketException ex) { Console.WriteLine("WebSocket接收错误: " + ex.ToString()); // 处理异常关闭场景 connection_closed(web_socket.CloseStatus.HasValue ? web_socket.CloseStatus.Value : null); } catch (Exception ex) { Console.WriteLine("未知错误: " + ex.ToString()); connection_closed(null); } }
调用方式
启动接收循环无需手动创建线程,直接在异步上下文或用Task.Run启动:
// 初始化并连接WebSocket后,启动接收循环 var cancellationTokenSource = new CancellationTokenSource(); // 用_接收任务,避免编译器警告,如需等待可保存任务对象 _ = ReceiveLoopAsync(cancellationTokenSource.Token); // 当需要主动停止时: // cancellationTokenSource.Cancel(); // await web_socket.CloseAsync(WebSocketCloseStatus.NormalClosure, "主动关闭", CancellationToken.None);
关键优化点
- 移除
Thread.Sleep,利用ReceiveAsync的异步等待特性,有数据时立即处理,无数据时异步释放线程资源。 - 复用缓冲区,减少内存分配开销。
- 用
CancellationToken控制循环生命周期,便于主动停止接收。 - 完整处理正常关闭(服务端发Close帧)和异常关闭(抛出WebSocketException)两种场景,确保连接状态正确更新。
内容的提问来源于stack exchange,提问作者FireCatMagic
相关产品推荐
相关产品推荐

