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

C#中使用同一端口收发UDP数据报的问题咨询

UDP同一端口收发问题及解决建议

问题描述

我使用端口2100同时收发UDP数据包,通过Wireshark确认:发送和接收的数据包都使用该端口,远程主机也返回了正确的响应数据,但EndReceive()函数只接收到我自己发送的原始数据报,收不到远程设备的回复。

我推测原因是:使用同一端口且监听IPAddress.Any,导致套接字接收到了自身发送的数据包。

我的需求是查询远程设备IP:发送包含设备序列号的广播查询包,对应设备会回复包含其IP的数据包。该远程设备仅响应来自端口2100的请求,且回复也发送到端口2100。

最初尝试client.Receive()函数,结果相同;切换为EndReceive()后问题仍未解决。想请教两个问题:

  • 有什么解决当前接收问题的建议?
  • 是否有办法确定PC发送数据报时使用的IP地址?

原始代码

static int numRcvdBytes;
static Byte[] rcvdBytes = new Byte[512];

static void SendRequest()
{
  numRcvdBytes = 0;
  var data = Encoding.ASCII.GetBytes("0C1>" + amp.serial + ";000,P \r\n");
  UdpClient client = new UdpClient();
  IPEndPoint lep = new IPEndPoint(IPAddress.Any, 2100);    // 设置源端口为2100
  IPEndPoint rep = new IPEndPoint(IPAddress.Parse(BroadcastIP), 2100); // 设置目标端口为2100
  client.ExclusiveAddressUse = false;
  client.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
  client.Client.Bind(lep);
  client.BeginReceive(DataReceived, client);
  client.Send(data, data.Length, rep);
  Thread.Sleep(50);   // 等待远程主机响应
  if (numRcvdBytes > 2)
  {
     for ( int b=0; b < numRcvdBytes-2; b++)
     {
        Console.Write("{0}", Convert.ToChar(rcvdBytes[b]) );
     }
  }
  else
  {
     LogEvent(tw, "   Serial# " + amp.serial + " Not Found!" );
  }
}

static void DataReceived(IAsyncResult ar)
{
   UdpClient c = (UdpClient)ar.AsyncState;
   IPEndPoint receivedIpEndPoint = new IPEndPoint(IPAddress.Any, 0);
   Byte[] receivedBytes = c.EndReceive(ar, ref receivedIpEndPoint);
   Array.Copy(receivedBytes, 0, rcvdBytes, 0, receivedBytes.Length);
   numRcvdBytes = receivedBytes.Length;
}

解决建议

1. 过滤自身发送的数据包

在DataReceived回调中,通过receivedIpEndPoint获取发送方的IP,判断是否为本机发送的数据包,若是则直接忽略,不处理。

2. 获取发送数据报的本机IP

发送数据包后,通过client.Client.LocalEndPoint可以获取系统实际使用的出口IP(因为绑定的是IPAddress.Any,系统会自动选择合适的网卡IP发送),代码如下:

IPAddress localSendIp = ((IPEndPoint)client.Client.LocalEndPoint).Address;

拿到这个IP后,在接收回调里对比收到的数据包来源IP,即可过滤掉自身发送的包。

3. 优化异步接收与资源管理

  • 不要用固定Thread.Sleep(50)等待响应,建议延长等待时间(比如1秒),或者用TaskCompletionSource实现异步等待,避免因为响应延迟导致接收不到数据。
  • 使用using语句包裹UdpClient,确保资源正确释放,同时避免在接收回调执行前提前释放客户端。

4. 补充Socket广播选项

发送广播包时,建议添加Broadcast选项,确保系统允许发送广播数据:

client.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, true);

修改后的代码示例

static int numRcvdBytes;
static Byte[] rcvdBytes = new Byte[512];
static IPAddress _localSendIp; // 存储发送时使用的本机IP

static void SendRequest()
{
    numRcvdBytes = 0;
    var data = Encoding.ASCII.GetBytes("0C1>" + amp.serial + ";000,P \r\n");
    
    // 使用using确保UdpClient资源正确释放
    using (UdpClient client = new UdpClient())
    {
        IPEndPoint lep = new IPEndPoint(IPAddress.Any, 2100);
        IPEndPoint rep = new IPEndPoint(IPAddress.Parse(BroadcastIP), 2100);
        
        client.ExclusiveAddressUse = false;
        client.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
        client.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, true);
        client.Client.Bind(lep);

        // 先发送数据包,再获取实际使用的本地IP
        client.Send(data, data.Length, rep);
        _localSendIp = ((IPEndPoint)client.Client.LocalEndPoint).Address;

        // 启动异步接收
        client.BeginReceive(DataReceived, client);
        
        // 延长等待时间,确保远程设备有足够时间响应
        Thread.Sleep(1000);

        if (numRcvdBytes > 2)
        {
            for (int b = 0; b < numRcvdBytes - 2; b++)
            {
                Console.Write("{0}", Convert.ToChar(rcvdBytes[b]));
            }
        }
        else
        {
            LogEvent(tw, "   Serial# " + amp.serial + " Not Found!");
        }
    }
}

static void DataReceived(IAsyncResult ar)
{
    UdpClient c = (UdpClient)ar.AsyncState;
    IPEndPoint receivedIpEndPoint = new IPEndPoint(IPAddress.Any, 0);
    Byte[] receivedBytes = c.EndReceive(ar, ref receivedIpEndPoint);

    // 过滤自身发送的数据包
    if (receivedIpEndPoint.Address.Equals(_localSendIp))
    {
        // 重新启动接收,等待下一个数据包
        c.BeginReceive(DataReceived, c);
        return;
    }

    // 处理远程设备的响应
    Array.Copy(receivedBytes, 0, rcvdBytes, 0, receivedBytes.Length);
    numRcvdBytes = receivedBytes.Length;
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 18:57:01