UDP客户端-接收排队数据包

问题描述

我正在开发UDP客户端PC应用程序。应该从四个以上的设备接收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);

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

解决方法

方法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秒钟后停止。这不是完全干净的代码,例如,如果您愿意,可以使整个过程异步。


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