Получение ответа (ответов) от числа N клиентов в ответ на широковещательный запрос по UDP

Я реализую своего рода средство поиска IP для конкретного типа сетевого мультимедийного устройства. Я хочу узнать все живые устройства того типа в LAN с их IP-адресом и другими деталями.

Устройство поступает по-своему обнаружения устройств.

Это работает следующим образом: клиент отправляет широковещательный запрос по LAN через UDP.
Номер целевого порта фиксируется.
В ответ все серверы в LAN, которые понимают формат этого запроса, ответят, это запрашивает обеспечение информации о себе.

Я широковещательно передаю сообщение запроса UDP с помощью sendto ().

Теперь моя проблема состоит в том, что я не знаю, сколько устройств (i.e.servers) ответит на запрос.

Сколько раз я должен буду назвать recvfrom ()?
Когда я узнаю это, я обработал ответ от всех устройств?
Или в целом, является recvfrom () правильным выбором для получения ответа с нескольких серверов?
Есть ли немного лучше (или КОРРЕКТНЫЙ, если я неправ здесь), способ выполнить то же?

Я программирую в C/C++, планируя кодировать и для Windows и для Linux.
Заранее большое спасибо.

Править: Таким образом с помощью всех мастеров сетевого программирования здесь, я нашел решение своей проблемы :)
выберите (), просто вещь для меня...
Большое спасибо ко всем Вам, кто вынул время для помощи мне

6
задан puffadder 3 March 2010 в 09:58
поделиться

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 () обычно используется с установкой соединения без установления соединения. сокеты режима, потому что он позволяет приложению извлекать адрес источника полученных данных.

2
ответ дан 17 December 2019 в 02:27
поделиться

Если вы не знаете, сколько серверов ответят, значит, вы не знаете, сколько раз вам нужно вызывать 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 справится с этим.

2
ответ дан 17 December 2019 в 02:27
поделиться

Используйте select (2) / poll (2) с таймаутом в цикле, уменьшая тайм-аут каждый раз, когда вы получаете ответ от устройства. Придется самому придумать подходящий тайм-аут.

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

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

2
ответ дан 17 December 2019 в 02:27
поделиться

Вы не можете знать. Это непостижимо.

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

Если я правильно понимаю, что UDP по своей сути ненадежен, вам придется немного повысить надежность поверх UDP:

  • Отправлять широковещательную рассылку каждые несколько секунд, так как устройства могут ее не принимать. каждый раз.
  • Возможно, вы не получите их ответов, но в следующий раз получите их.
  • Ответ подтверждает, что устройство живо.
  • Дождитесь "n" неответов, прежде чем объявлять устройство мертвым.

Знаете ли вы максимальное количество возможных устройств? Если вы это сделаете, вы можете обнаружить, что вам придется вызывать recvfrom () много раз.

0
ответ дан 17 December 2019 в 02:27
поделиться