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

.NET 6中ClientWebSocket.ReceiveAsync仅接收一次数据后无响应

问题:.NET 6中ClientWebSocket定时轮询仅能接收一次数据

在.NET 6环境下使用C#的System.Net.WebSockets.ClientWebSocket类,设置了16ms触发的定时器轮询WebSocket接收数据,收到数据包后反序列化为JSON并生成事件,但该逻辑仅能成功执行一次。

添加了测试打印语句:

Console.WriteLine("Awaiting started");

和

Console.WriteLine("Awaiting ended");

预期流程:轮询时打印"Awaiting started",完成接收后打印"Awaiting ended",非空数据包则执行反序列化。相关代码如下:

private async void poll(Object? sender, ElapsedEventArgs event_args) 
{
    switch (web_socket.State) 
    {
        case WebSocketState.Open: 
            {
                if (socket_connected == false) 
                {
                    socket_connected = true;
                }

                try 
                {
                    // ReceiveAsync automatically polls and receives the packet.
                    Memory<Byte> memory = new Memory<Byte>(new Byte[1024]);
                    Console.WriteLine("Awaiting started");
                    ValueWebSocketReceiveResult result = await web_socket.ReceiveAsync(memory, CancellationToken.None);
                    Console.WriteLine("Awaiting ended");

                    // Only attempt deserialization if a packet was received, aka the buffer is not empty.
                    if (result.Count != 0) 
                    {
                        deserialize_event(memory.Slice(0, result.Count).ToArray());
                    }
                } 
                catch (Exception exception) 
                { 
                    Console.WriteLine("Pusher polling error: " + exception.ToString());
                }
            } 
            break;

        case WebSocketState.Closed: 
            {
                connection_closed(web_socket.CloseStatus);
            } 
            break;
    }
}

实际现象:仅能接收一次数据包,仅打印一次"Awaiting ended",后续所有ReceiveAsync调用均无法返回。


问题原因

  1. 定时器与异步方法冲突:定时器触发的poll是async void方法,第一次await ReceiveAsync会异步挂起,定时器再次触发时会并发调用poll,而ClientWebSocket的ReceiveAsync不支持并发操作,后续调用会被阻塞。
  2. ReceiveAsync特性:ReceiveAsync会一直挂起等待数据,定时器16ms一次的触发会导致多次调用堆积,后续调用无法获取执行机会。

解决方法

方法一:用持续异步循环替代定时器

这是WebSocket接收的标准处理方式,能保证同一时间只有一个ReceiveAsync执行:

private async Task StartWebSocketReceiving()
{
    while (web_socket.State == WebSocketState.Open)
    {
        try
        {
            Memory<byte> memory = new Memory<byte>(new byte[1024]);
            Console.WriteLine("Awaiting started");
            ValueWebSocketReceiveResult result = await web_socket.ReceiveAsync(memory, CancellationToken.None);
            Console.WriteLine("Awaiting ended");

            if (result.Count != 0)
            {
                deserialize_event(memory.Slice(0, result.Count).ToArray());
            }

            // 处理关闭帧
            if (result.MessageType == WebSocketMessageType.Close)
            {
                await web_socket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None);
                connection_closed(web_socket.CloseStatus);
                break;
            }
        }
        catch (Exception exception)
        {
            Console.WriteLine("Pusher polling error: " + exception.ToString());
            break;
        }
    }
}

在WebSocket连接成功后启动该任务:

// 连接成功后调用
_ = StartWebSocketReceiving();

方法二:添加并发控制保留定时器

如果必须用定时器,通过标志位保证同一时间只有一个接收操作在执行:

private bool _isReceiving = false;
private async void poll(Object? sender, ElapsedEventArgs event_args) 
{
    if (_isReceiving) return;
    
    switch (web_socket.State) 
    {
        case WebSocketState.Open: 
            {
                if (socket_connected == false) 
                {
                    socket_connected = true;
                }

                try 
                {
                    _isReceiving = true;
                    Memory<Byte> memory = new Memory<Byte>(new Byte[1024]);
                    Console.WriteLine("Awaiting started");
                    ValueWebSocketReceiveResult result = await web_socket.ReceiveAsync(memory, CancellationToken.None);
                    Console.WriteLine("Awaiting ended");

                    if (result.Count != 0) 
                    {
                        deserialize_event(memory.Slice(0, result.Count).ToArray());
                    }

                    if (result.MessageType == WebSocketMessageType.Close)
                    {
                        await web_socket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None);
                        connection_closed(web_socket.CloseStatus);
                    }
                } 
                catch (Exception exception) 
                { 
                    Console.WriteLine("Pusher polling error: " + exception.ToString());
                }
                finally
                {
                    _isReceiving = false;
                }
            } 
            break;

        case WebSocketState.Closed: 
            {
                connection_closed(web_socket.CloseStatus);
            } 
            break;
    }
}

关键说明

  • ClientWebSocket的ReceiveAsync不支持并发调用,必须保证同一时间只有一个接收操作在进行。
  • 异步循环比定时器更可靠,它会在每次接收完成后立即等待下一个数据,无需依赖固定间隔,能更高效处理WebSocket数据流。

内容的提问来源于stack exchange,提问作者FireCatMagic

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 22:55:14