【问题标题】:UDP Client - Reception of enqueued packetsUDP 客户端 - 接收排队的数据包
【发布时间】:2023-09-12 04:12:02
【问题描述】:

我正在开发一个 UDP 客户端 PC 应用程序。它应该从 4 个以上的设备接收 UDP 数据报。
系统行为如下:

  • 多个设备通过固定端口 (11000) 上的 UDP 广播相互通信,形成一个不连接到互联网的个人区域网络。
  • PC 应用程序在连接到同一网络的计算机上执行。
  • PC 应用程序侦听 11000 端口上的 UDP 广播以发现设备。
  • 当从 PC 应用程序接收到特定命令时,该设备会进入不同的执行模式,而其他设备会继续广播其数据包。
  • 当个人局域网中只有一个设备时,这会以所需的方式运行。

当网络中有两个或更多设备时,我遇到了一个奇怪的问题,例如:

  • 我使用发现的设备列表将endPoint 设置为所需设备的所需 IP 地址和端口。
  • 我调用myUDP.Receive(ref endPoint);接收UDP数据报

这将返回网络中第二个设备广播的数据报,而不是返回来自我尝试与之通信的设备的响应。我已经使用 Wireshark 验证了响应是从设备发送的。

我尝试循环有限次以获得所需的数据报。

// Some code which initializes the endPoint with desired IP Address and Port
...
// Some code which sends the data
...
// Some code which sets the IP Address of the device from which the response is expected
selectedIPAddress = IPAddress.Parse(labelIPAddressSettings.Text.Trim());
copyendPoint = endPoint;
// Listen to response
do
{
    rexdDatagram = myUDP.Receive(ref endPoint);
    if (endPoint.Address != selectedIPAddress)
    {
        // This datagram is not from the desired device
        // Restore to the desired endpoint
        endPoint = copyendPoint;
        // Not sure if there is way to discard this enqueued datagram
    }
    
    i_timeout = i_timeout + 1;
    if (i_timeout == 10)
    {
        // Datagram from the desired device has not been received 
        break;
    }
    // Not sure if the thread needs to sleep, debugging..
    Thread.Sleep(1000);
} while (1);

问题: 我的代码在排队数据报中循环是否正确?有没有办法丢弃以前的数据报并重新开始?

【问题讨论】:

  • 你试过没有Thread.Sleep或者至少很短的睡眠时间吗?而且,这里没有代码可以处理如果它预期的端点...
  • 方法UdpClient.Receive 上的参数remoteEP 并不是为了指定从哪个远程地址接收,而是指定从哪个远程地址接收到东西。这意味着您应该始终将new IPEndPoint(IPAddress.Any, 0) 传递给它
  • @Fildor 在删除 Thread.Sleep 并处理预期端点后仍然面临同样的问题。现在能够解决这个问题。感谢您的评论。

标签: c# .net winforms udpclient


【解决方案1】:

UdpClient.Receive 方法上的参数remoteEP 并非用于指定从哪个远程端点接收数据,而是指定哪个远程端点发送数据。您不能选择性地仅从特定端点接收。

相反,您必须接收来自所有人的所有内容,并丢弃不是从您所需的远程端点发送的包。你可以这样做:

byte[] receivedData = null;
var attempts = 0;

while (attempts < 10)
{
    var recvEp = new IPEndPoint(IPAddress.Any, 0);
    readData = myUDP.Receive(ref recvEp);
  
    if (recvEp.Address == selectedIPAddress)
    {
       // We received data from the correct remote source
       receivedData = readData;
       break;
    }

    attempts++;
}

此代码将从任何地方接收数据,如果在 10 次尝试内没有从正确的端点接收数据,它将停止。导致receivedData 为空。

您可能希望将代码转换为等待一定量的时间,而不是一定量的尝试,以增加实际接收某些东西的机会。可以这样做:

var start = DateTime.Now;
byte[] receivedData = null;

while((DateTime.Now - start).TotalSeconds < 10)
{
    var recvEp = new IPEndPoint(IPAddress.Any, 0);
    readData = myUDP.Receive(ref recvEp);
  
    if (recvEp.Address == selectedIPAddress)
    {
       // We received data from the correct remote source
       receivedData = readData;
       break;
    }
}

此代码将尝试 10 秒,如果没有收到任何内容,则在 10 秒后停止。这不是完全干净的代码,例如,如果你愿意,你可以让整个事情异步。


注意: 两个代码 sn-ps 都可能导致无限循环,因为只要没有任何传入数据,myUDP.Receive(ref recvEp) 就会阻塞。因此,如果您的所有远程端点决定同时停止发送数据,则接收调用将永远不会返回

【讨论】:

  • 在一定时间内处理 Receive 是个好主意。使用关闭所有现有连接来解决此问题。感谢您对 MindSwipe 的帮助。
最近更新 更多