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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 17:53:13