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

C#中UdpClient无法接收数据问题求助

解决UdpClient异步接收数据失败的问题

我仔细看了你的代码,发现问题主要出在异步回调的复用逻辑和接收端点的使用方式上,咱们一步步拆解并修复:

核心问题分析

  1. 回调逻辑的状态混淆
    你用Transmit这个布尔变量来区分发送/接收的回调,但异步操作的执行顺序是不确定的,很容易出现状态判断错误——比如接收完成的回调可能被误判为发送完成,导致跳过数据处理;同时这个变量没有线程同步,多线程访问时会出现竞态问题。

  2. 接收端点的错误复用
    你复用类成员Listen作为EndReceive的ref参数,而UdpClient的EndReceive需要一个空白的IPEndPoint(通常是new IPEndPoint(IPAddress.Any, 0))来填充发送者的地址信息。复用同一个端点对象可能导致后续接收操作出现异常或数据丢失。

修复后的代码示例

我重构了你的代码,拆分了发送/接收回调,优化了线程安全和接收逻辑:

using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;

public class UdpReceiver
{
    private UdpClient _udpClient;
    private IPEndPoint _remoteDestination;
    // 用Interlocked保证发送状态的线程安全
    private int _isTransmitting = 0;

    public UdpReceiver(string remoteHostname, int remotePort, int localPort)
    {
        _remoteDestination = new IPEndPoint(IPAddress.Parse(remoteHostname), remotePort);
        _udpClient = new UdpClient(localPort);
        // 启动异步接收循环
        StartAsyncReceive();
    }

    public void Send(byte[] data)
    {
        // 原子操作判断是否正在发送,避免并发发送冲突
        if (Interlocked.CompareExchange(ref _isTransmitting, 1, 0) == 0)
        {
            _udpClient.BeginSend(data, data.Length, _remoteDestination, SendCompletedCallback, null);
        }
    }

    private void StartAsyncReceive()
    {
        // 每次接收都创建新的空白端点,用于获取发送者的地址信息
        IPEndPoint senderEndpoint = new IPEndPoint(IPAddress.Any, 0);
        _udpClient.BeginReceive(ReceiveCompletedCallback, senderEndpoint);
    }

    private void ReceiveCompletedCallback(IAsyncResult result)
    {
        IPEndPoint senderEndpoint = (IPEndPoint)result.AsyncState;
        try
        {
            byte[] receivedData = _udpClient.EndReceive(result, ref senderEndpoint);
            System.Diagnostics.Debug.WriteLine($"收到 {receivedData.Length} 字节数据,来自 {senderEndpoint}");
            
            // 在这里添加你的数据处理逻辑...
            
            // 继续接收下一个数据包
            StartAsyncReceive();
        }
        catch (Exception ex)
        {
            System.Diagnostics.Debug.WriteLine($"接收失败: {ex.Message}");
            // 如果客户端未关闭,重启接收循环
            if (!_udpClient.Client.IsClosed)
            {
                StartAsyncReceive();
            }
        }
    }

    private void SendCompletedCallback(IAsyncResult result)
    {
        try
        {
            int sentBytes = _udpClient.EndSend(result);
            System.Diagnostics.Debug.WriteLine($"成功发送 {sentBytes} 字节数据");
        }
        catch (Exception ex)
        {
            System.Diagnostics.Debug.WriteLine($"发送失败: {ex.Message}");
        }
        finally
        {
            // 重置发送状态,允许下次发送
            Interlocked.Exchange(ref _isTransmitting, 0);
        }
    }
}

关键修改点说明

  • 拆分回调函数:将发送和接收的回调分开,彻底避免状态混淆的问题;
  • 线程安全的发送控制:用Interlocked类的原子操作替代简单的布尔变量,确保同一时间只有一个发送操作在执行;
  • 独立的接收端点:每次接收都创建新的IPEndPoint,保证能正确获取发送者的地址,同时避免多线程下的对象冲突;
  • 异常处理与容错:添加了异常捕获,即使出现错误也能自动重启接收循环,提升稳定性;
  • 代码规范优化:调整了变量命名,更符合C#的编码习惯,可读性更强。

额外排查建议

  1. 确认你的UdpClient没有被意外调用Close()或Dispose();
  2. 检查应用的网络权限(比如桌面应用需要在Windows防火墙中开放对应端口);
  3. 如果是在多线程环境下使用,确保对UdpClient的操作是线程安全的(UdpClient本身不是线程安全的,避免多个线程同时调用Send/Receive)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 09:08:16