#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.