UDP клиент — прием поставленных в очередь пакетов

#c# #.net #winforms #udpclient

#c# #.net #winforms #udpclient

Вопрос:

Я разрабатываю клиентское приложение UDP для ПК. Предполагается, что он принимает UDP-дейтаграммы более чем с 4 устройств.
Система ведет себя следующим образом:

  • Несколько устройств обмениваются данными друг с другом посредством UDP-трансляций на фиксированном порту (11000), образуя персональную локальную сеть без подключения к Интернету.
  • Приложение для ПК выполняется на компьютере, подключенном к той же сети.
  • Приложение для ПК прослушивает широковещательные передачи UDP через порт 11000, чтобы обнаружить устройства.
  • Когда определенная команда принимается из приложения ПК, это устройство переходит в другой режим выполнения, в то время как другие устройства продолжают передавать свои пакеты.
  • Это работает желаемым образом, когда в персональной сети есть только одно устройство.

Я сталкиваюсь со странной проблемой, когда в сети есть два или более устройств, таких, что:

  • Я установил 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);
  

Вопрос:
Правильно ли мой код выполняет цикл внутри поставленных в очередь дейтаграмм? Есть ли способ отбросить предыдущие дейтаграммы и начать заново?

Комментарии:

1. Вы пробовали без Thread.Sleep или, по крайней мере, с очень коротким временем ожидания? И также, здесь нет кода, который обрабатывает случай, если это является ожидаемой конечной точкой…

2. Параметр remoteEP метода UdpClient.Receive предназначен не для указания, с какого удаленного адреса получать, а скорее для указания, с какого удаленного адреса что-либо было получено. Это означает, что вы всегда должны переходить new IPEndPoint(IPAddress.Any, 0) к нему

3. @Fildor по-прежнему сталкивался с той же проблемой после удаления потока. Переход в спящий режим и обработка ожидаемой конечной точки. Теперь удалось устранить эту проблему. Спасибо за ваш комментарий.

Ответ №1:

Параметр remoteEP метода UdpClient.Receive предназначен не для указания, с какой удаленной конечной точки получать, а скорее для указания, какая удаленная конечная точка отправила данные. Вы не можете выборочно получать только с определенной конечной точки.

Вместо этого вам придется получать все от всех и отбрасывать пакеты, которые не были отправлены с желаемой удаленной конечной точки. Вы можете сделать это следующим образом:

 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 получается значение null.

Возможно, вы захотите преобразовать свой код так, чтобы он ожидал определенное количество времени, а не определенное количество попыток, чтобы увеличить шансы на фактическое получение чего-либо. Это можно было бы сделать следующим образом:

 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) будут блокироваться до тех пор, пока не поступят какие-либо входящие данные. Таким образом, если все ваши удаленные конечные точки решат прекратить отправку данных одновременно, вызов receive никогда не вернется

Комментарии:

1. Неплохо обрабатывать прием за определенное время. Устраните проблему, закрыв все существующие соединения. Спасибо за вашу помощь, MindSwipe.