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

如何停止处于阻塞监听状态的UdpClient实例?

如何停止阻塞的UdpClient/Socket监听?

针对你遇到的阻塞式UDP监听无法通过CancellationToken停止的问题,这里提供几种实用的解决方案:


方法1:通过关闭UdpClient触发异常终止监听

阻塞状态的UdpClient.Receive()或ReceiveAsync()会在调用Close()时抛出SocketException,我们可以捕获该异常来优雅终止监听任务,同时配合标记变量避免重复操作。

示例代码:

private UdpClient _udpClient;
private bool _isStopping;

// 启动UDP监听任务
void StartUdpListening(IPAddress localAddress)
{
    _isStopping = false;
    _udpClient = new UdpClient(new IPEndPoint(localAddress, 12345));
    
    Task.Run(async () =>
    {
        try
        {
            while (!_isStopping)
            {
                // 异步接收(同步Receive逻辑一致)
                var receiveResult = await _udpClient.ReceiveAsync();
                // 处理接收到的数据
                // ProcessReceivedData(receiveResult.Buffer, receiveResult.RemoteEndPoint);
            }
        }
        catch (SocketException ex)
        {
            // 仅处理非预期异常,关闭导致的中断异常(错误码10004)无需处理
            if (ex.SocketErrorCode != SocketError.Interrupted)
            {
                // 记录或处理其他网络异常
            }
        }
        finally
        {
            _udpClient?.Dispose();
        }
    });
}

// 网络变化时停止监听
private void OnNetworkAddressChanged(object sender, EventArgs e)
{
    _isStopping = true;
    _udpClient?.Close(); // 关闭客户端触发阻塞接收的异常,退出循环
}

方法2:使用Socket.Poll实现轮询式监听

直接使用底层Socket,通过Poll()方法定期检查是否有数据到达,同时轮询取消标记,避免完全阻塞。

示例代码:

private Socket _udpSocket;
private CancellationTokenSource _cts;

// 启动监听
void StartSocketListening(IPAddress localAddress)
{
    _cts = new CancellationTokenSource();
    _udpSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
    _udpSocket.Bind(new IPEndPoint(localAddress, 12345));
    
    Task.Run(() =>
    {
        byte[] buffer = new byte[1024];
        try
        {
            while (!_cts.Token.IsCancellationRequested)
            {
                // 每隔100ms检查一次是否有数据可读,同时响应取消标记
                if (_udpSocket.Poll(100000, SelectMode.SelectRead))
                {
                    int receivedBytes = _udpSocket.Receive(buffer);
                    // 处理数据
                    // ProcessReceivedData(buffer, receivedBytes);
                }
            }
        }
        finally
        {
            _udpSocket?.Close();
            _cts?.Dispose();
        }
    });
}

// 网络变化时取消监听
private void OnNetworkAddressChanged(object sender, EventArgs e)
{
    _cts.Cancel();
}

方法3:异步接收+取消令牌回调

对于支持.NET 5及以上版本的环境,UdpClient.ReceiveAsync()可直接传入CancellationToken,同时注册取消回调确保客户端被关闭:

private UdpClient _udpClient;
private CancellationTokenSource _cts;

void StartAsyncUdpListening(IPAddress localAddress)
{
    _cts = new CancellationTokenSource();
    _udpClient = new UdpClient(new IPEndPoint(localAddress, 12345));
    
    // 注册取消回调:当令牌触发时关闭客户端
    _cts.Token.Register(() => _udpClient.Close());
    
    Task.Run(async () =>
    {
        try
        {
            while (!_cts.Token.IsCancellationRequested)
            {
                var receiveResult = await _udpClient.ReceiveAsync(_cts.Token);
                // 处理数据
            }
        }
        catch (OperationCanceledException)
        {
            // 正常取消,无需额外处理
        }
        catch (SocketException ex)
        {
            if (ex.SocketErrorCode != SocketError.Interrupted)
            {
                // 处理其他异常
            }
        }
        finally
        {
            _udpClient?.Dispose();
            _cts?.Dispose();
        }
    });
}

// 网络变化时取消
private void OnNetworkAddressChanged(object sender, EventArgs e)
{
    _cts.Cancel();
}

注意事项

  • 所有操作需保证线程安全,修改监听状态或操作客户端实例时,可通过锁或原子变量避免冲突。
  • 客户端关闭后务必调用Dispose()释放资源,防止内存泄漏。
  • 异常捕获时需区分"主动关闭导致的中断"和"其他网络错误",避免误处理正常终止逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 10:45:49