Получение ответа(ов) от N числа клиентов в ответ на широковещательный запрос по UDP
я реализую своего рода IP finder для определенного типа сетевого мультимедийного устройства. Я хочу узнать все живые устройства этого типа в локальной сети, с их IP-адресом и другими деталями.
устройство имеет свой собственный способ обнаружения устройств.
Он работает следующим образом:
Клиент отправляет широковещательный запрос по сети через UDP.
Номер порта назначения фиксирован.
В ответ все серверы в локальной сети, которые понимают формат этого запрос ответит на этот запрос предоставлением информации о себе.
Я передаю сообщение запроса UDP с помощью sendto ().
теперь моя проблема в том, что я не знаю, сколько устройств (i.e.серверы) ответит на запрос.
сколько раз мне придется вызывать recvfrom ()?
Когда я узнаю, что я обработал ответ со всех устройств?
Или вообще, recvfrom () правильный выбор для получение ответа от нескольких серверов?
Есть ли лучший (или правильный, если я ошибаюсь здесь) способ достижения того же самого?
я программирую на C / C++, планируя кодировать как для Windows, так и для Linux.
Большое спасибо заранее.
изменить:
Поэтому с помощью всех мастеров сетевого программирования здесь я нашел решение своей проблемы:)
select () - это как раз то, что мне нужно...
Большое спасибо всем, кто потратил время чтобы помочь мне!--1-->
4 ответов
сколько раз мне придется вызывать recvfrom ()? Когда я узнаю, что я обработал ответ со всех устройств/серверов?
если вы не знаете количество устройств/серверов, вы не можете знать, сколько раз вам нужно позвонить recvfrom()
или когда вы обработали все ответы.
можно использовать select()
петли (до тайм-аута) и вызов recvfrom()
когда данные доступны для чтения. Это может быть в основном потоке или отдельная нить.
если данные поступают быстрее, чем они могут быть обработаны, вы потеряете датаграммы. Это будет во многом зависеть от скорости, с которой данные анализируются и сохраняются после их получения. Если обработка данных является интенсивной операцией, может потребоваться выполнить обработку в отдельном потоке или сохранить данные до истечения времени цикла приема, а затем продолжить обработку.
поскольку UDP ненадежен, цикл для ретрансляции несколько раз должен помогите учесть некоторые потери, и обработка должна учитывать дубликаты.
следующий псевдокод - это то, как я могу подойти к проблеме:
/* get socket to receive responses */
sd = socket( ... );
do
{
/* set receive timeout */
timeout.tv_sec = 5;
/* broadcast request */
sendto( ... );
/* wait for responses (or timeout) */
while(select(sd+1, &readfds, NULL, NULL, &timeout) > 0)
{
/* receive the response */
recvfrom( ... );
/* process the response (or queue for another thread / later processing) */
...
/* reset receive timeout */
timeout.tv_sec = 5;
}
/* process any response queued for later (and not another thread) */
} while (necessary);
или вообще, recvfrom () правильный выбор для получения ответа от нескольких серверов?
recvfrom()
обычно используется с сокетами без подключения, поскольку это позволяет приложению извлекать исходный адрес полученных данных.
использовать select(2)/poll(2)
с таймаутом в цикле, уменьшая таймаут каждый раз, когда вы получаете ответ от устройства. Вам придется самому придумать подходящий тайм-аут.
кроме того, если вы можете распознать/проанализировать ответное сообщение обнаружения, просто добавьте устройство в список после получения такого сообщения.
вы, вероятно, все равно придется заниматься тайм-аутов для устройств для регистрации, но не позднее.
Если вы не знаете, сколько серверов будут отвечать, то вы не знаете, сколько раз вам нужно вызвать recvfrom(). Я бы, вероятно, справился с этим с помощью цикла select () с подходящим таймаутом, что-то вроде следующего, которое полностью непроверено и, вероятно, полно глупых ошибок:
/* create and bind socket */
fd_set fds;
struct timeval tv;
tv.tv_sec = 2;
tv.tv_usec = 0;
FD_ZERO(&fds);
FD_SET(sock, &fds);
int ret;
while((ret = select(sock + 1, &fds, NULL, NULL, &tv)) > 0) {
char buf[BUFLEN];
struct sockaddr addr;
if(recvfrom(sock, buf, BUFLEN, MSG_DONTWAIT, &addr, sizeof(struct sockaddr)) > 0) {
/* handle response */
} else {
/* handle error */
}
}
if(ret < 0) {
/* handle error */
} else {
/* select() timed out; we're theoretically done */
}
Это будет продолжать вызывать recvfrom (), пока ответ не будет получен в течение 2 секунд, что, конечно, означает, что он будет блокировать как минимум 2 секунды. В зависимости от базовый протокол, вы, вероятно, можете уйти с гораздо более коротким таймаутом; действительно, Вы можете уменьшить его на каждом ответе. Для поиска оптимальной конфигурации потребуется некоторое тестирование и настройка. Вам не нужно беспокоиться о том, что серверы отвечают одновременно; уровень Ethernet будет обрабатывать это.
вы не можете знать. Его непознаваемое.
предположительно, из вашего описания: I want to find out all the alive devices
, устройства могут переходить от мертвых к живым и обратно в любое время, когда они хотят. Это означает, что вам придется опрашивать непрерывно: т. е. отправлять широковещательный запрос каждые несколько секунд (но не слишком часто) и видеть, кто отвечает.
Если я правильно понял, что UDP по своей сути ненадежен, вам придется ретро-соответствовать некоторой надежности поверх UDP:
- отправить трансляцию каждые несколько секунд, так как устройства не могут получать его каждый раз.
- вы не можете получить их ответы, но вы можете в следующий раз.
- ответ подтверждает, что устройство живо.
- дождитесь " n " не-ответов, прежде чем объявить устройство мертвым.
знаете ли вы максимальное количество возможных устройств? Если вы это сделаете, вы можете обнаружить, что вам нужно вызвать recvfrom() столько раз.