Получение ответа (ов) от N числа клиентов в ответ на широковещательный запрос по UDP - PullRequest
6 голосов
/ 02 марта 2010

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

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

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

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

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

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

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

Edit: Итак, с помощью всех мастеров сетевого программирования я нашел решение моей проблемы:)
select () - это то, что мне нужно ...
Большое спасибо всем, кто нашел время, чтобы помочь мне

Ответы [ 4 ]

2 голосов
/ 02 марта 2010

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

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

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

2 голосов
/ 02 марта 2010

Если вы не знаете, сколько серверов будет отвечать, то вы не знаете, сколько раз вам нужно вызывать 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 голосов
/ 02 марта 2010

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

0 голосов
/ 02 марта 2010

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

Предположительно, из вашего описания: I want to find out all the alive devices, устройства могут переходить из мертвых в живые и обратно в любое время, когда захотят. Это означает, что вам придется опрашивать постоянно: отправлять запрос на рассылку каждые несколько секунд (но не слишком часто) и видеть, кто отвечает.

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

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

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

...