C# WebSocket Server不响应Ping请求的解决方案咨询
无需SignalR的C# WebSocket Server实现Ping响应与客户端断开检测
你的核心问题是没有持续监听WebSocket的输入帧——Ping这类控制帧必须通过ReceiveAsync才能被捕获,而单次调用ReceiveAsync只能处理一次消息,之后的Ping帧就没人处理了,导致客户端收不到Pong而断开。同时,不能用Thread.Sleep阻塞异步逻辑,会导致整个处理线程被卡住。
下面是修复后的完整代码,实现了持续监听消息、自动响应Ping、检测客户端断开的功能:
using System.Net.WebSockets; using System.Text; class Program { static async Task ClientHandler(HttpContext context) { using WebSocket socket = await context.WebSockets.AcceptWebSocketAsync(); Console.WriteLine("WebSocket客户端已连接,子协议:{0}", socket.SubProtocol); var buffer = new byte[1024 * 4]; var cancellationTokenSource = new CancellationTokenSource(); // 启动独立的接收任务,和发送逻辑并行执行 var receiveTask = Task.Run(async () => { try { while (socket.State == WebSocketState.Open) { var result = await socket.ReceiveAsync(new ArraySegment<byte>(buffer), cancellationTokenSource.Token); // 处理控制帧 switch (result.MessageType) { case WebSocketMessageType.Ping: // 自动回复Pong帧,数据和Ping一致 await socket.SendAsync( new ArraySegment<byte>(buffer, 0, result.Count), WebSocketMessageType.Pong, result.EndOfMessage, cancellationTokenSource.Token); Console.WriteLine("已响应客户端Ping"); break; case WebSocketMessageType.Close: await socket.CloseAsync( WebSocketCloseStatus.NormalClosure, "客户端发起关闭", cancellationTokenSource.Token); Console.WriteLine("客户端已断开连接"); break; case WebSocketMessageType.Text: // 处理业务文本消息 string message = Encoding.UTF8.GetString(buffer, 0, result.Count); Console.WriteLine("收到客户端消息:{0}", message); break; } } } catch (WebSocketException ex) { // 捕获连接异常,比如客户端强制断开 Console.WriteLine("连接异常:{0}", ex.Message); } finally { cancellationTokenSource.Cancel(); // 通知发送任务停止 } }); // 发送逻辑,循环发送消息直到连接关闭或收到取消信号 try { while (!cancellationTokenSource.Token.IsCancellationRequested && socket.State == WebSocketState.Open) { Console.WriteLine("正在发送消息..."); byte[] msg = Encoding.UTF8.GetBytes("Hello, World!"); await socket.SendAsync( new ArraySegment<byte>(msg), WebSocketMessageType.Text, true, cancellationTokenSource.Token); // 用异步延迟替代Thread.Sleep,避免阻塞线程 await Task.Delay(200, cancellationTokenSource.Token); } } catch (OperationCanceledException) { // 发送任务被取消,正常退出 } catch (WebSocketException ex) { Console.WriteLine("发送消息异常:{0}", ex.Message); } // 等待接收任务完成 await receiveTask; Console.WriteLine("连接已关闭"); } static void Main() { var app = WebApplication.CreateBuilder().Build(); app.UseWebSockets(); app.Use(async (context, next) => { if (context.Request.Path == "/") { if (context.WebSockets.IsWebSocketRequest) await ClientHandler(context); else context.Response.StatusCode = StatusCodes.Status400BadRequest; } else await next(context); }); app.Run("http://127.0.0.1:8001"); } }
关键修复点说明:
- 分离接收与发送逻辑:用
Task.Run启动独立的接收任务,和发送逻辑并行执行,避免ReceiveAsync阻塞发送流程。 - 处理控制帧:通过
WebSocketReceiveResult.MessageType判断帧类型,收到Ping时立即回复Pong,符合WebSocket协议要求。 - 异步延迟替代Thread.Sleep:用
Task.Delay替代Thread.Sleep,不会阻塞线程池线程,保证异步逻辑的响应性。 - 取消令牌协调任务:用
CancellationTokenSource协调接收和发送任务,当一方出现异常或连接关闭时,另一方能及时停止。 - 异常捕获:捕获
WebSocketException来检测客户端强制断开的情况,比如网络中断、客户端直接关闭等。
关于底层Socket的说明:
System.Net.WebSockets.WebSocket是抽象类,底层实现并没有暴露原始的Socket对象,所以无法直接调用Socket.Available。但通过上述异步循环监听的方式,完全可以满足Ping响应和断开检测的需求,不需要操作底层Socket。
内容的提问来源于stack exchange,提问作者Christian
相关产品推荐
相关产品推荐

