Получение ответа(ов) от 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() столько раз.