Как очистить входной буфер сокета UDP в C?

Как очистить входной буфер (если он вообще существует) UDP-сокета в C?

Я работаю над встроенной средой Linux и использую C для создания собственного приложения. В одной сети есть несколько таких встроенных машин, и когда на одной из них происходит событие (давайте назовем его ИНФОРМАЦИОННО-ИНФОРМАЦИОННЫМ ИНФОРМАЦИЕЙ), ИНФОРМАЦИОННО-ИНФОРМАЦИОННЫЙ ИНФОРМАЦИОННЫЙ ИНФОРМАТОР должен отправить сетевое сообщение на сеть (в том числе ИНФОРМАТОР) знает о событии и выполняет какие-то действия в соответствии с ним. Кстати, я использую сокет UDP...

Вот псевдокод для него:

main
{
    startNetworkListenerThread( networkListenerFunction );

    while( not received any SIGTERM or such )
    {
        localEventInfo = checkIfTheLocalEventOccured();
        broadcastOnNetwork( localEventInfo );
    }
}

networkListenerFunction
{
    bindSocket;

    while( not SIGTERM )
    {
// THIS IS WHERE I WANT TO FLUSH THE RECV BUFFER...
        recv_data = recvfrom( socket );
        if( validate recv data )
        {
            startExecuteLocalAction;
            sleep( 5 );
            stopExecuteLocalAction;
        }
    }
}

Вот как я ожидаю и хочу работать с этим кодом:

1. LOCAL_EVENT occured
2. Broadcasted LOCAL_EVENT_INFO on network
3. All machines received EVENT_INFO, including the original broadcaster
4. All machines started executing the local action, including the original broadcaster
5. All machines' network listener(thread)s are sleeping
6. Another LOCAL_EVENT2 occured
7. Since all machines' listener are sleeping, LOCAL_EVENT2 is ignored
8. All machines' network listener(thread)s are now active again
9. GO BACK TO 1 / RESTART CYCLE
RESULT = TOTAL 2 EVENTS, 1 IGNORED

Как это работает на самом деле:

1. LOCAL_EVENT occured
2. Broadcasted LOCAL_EVENT_INFO on network
3. All machines received EVENT_INFO, including the original broadcaster
4. All machines started executing the local action, including the original broadcaster
5. All machines' network listener(thread)s are sleeping
6. Another LOCAL_EVENT2 occured
7. Eventhough all machines' listener are sleeping; LOCAL_EVENT2 is queued  SOMEHOW
8. All machines' network listener(thread)s are now active again
9. All machines received EVENT_INFO2 and executed local actions again, slept and reactivated
10. GO BACK TO 1 / RESTART CYCLE
RESULT = TOTAL 2 EVENTS, 0 IGNORED

tl,dr: пакеты/сообщения/рассылки UDP, отправленные в уже привязанный сокет, чей родительский поток находится в спящем режиме в момент доставки; каким-то образом ставятся в очередь/буферизуются и доставляются при следующем вызове recvfrom на указанном сокете.

Я хочу, чтобы эти UDP-трансляции игнорировались, поэтому я подумал об очистке буфера приема (очевидно, не того, который я задаю в качестве параметра методу recvfrom), если он существует, до вызова recvfrom. Как я могу это сделать? или какой путь я должен следовать?


person Cihan Keser    schedule 19.02.2010    source источник


Ответы (2)


Обратите внимание, что понятие «сброс» применяется только к выводу. Сброс очищает буфер и гарантирует, что все в нем будет отправлено по назначению. Что касается входного буфера, данные уже находятся в месте назначения. Входные буферы можно считывать или очищать, но не «сбрасывать».

Если вы просто хотите убедиться, что вы прочитали все во входном буфере, вам нужна неблокирующая операция чтения. Если вы попытаетесь это сделать, а входных данных нет, он должен вернуть ошибку.

person bta    schedule 19.02.2010
comment
Пояснение: в тот момент, когда вы хотите очистить входной буфер, выполните циклическое неблокирующее чтение. Если есть данные, игнорируйте их и продолжайте читать. Если данных нет (где @bta говорит вернуть ошибку), вы игнорируете ошибку возврата, и ваш буфер сбрасывается. - person mpez0; 20.02.2010
comment
Моя идея и позиционирование очистки/очистки буфера ввода/приема могут быть неправильными, но; то, что я в основном хочу сделать, это игнорировать любые сообщения, отправленные во время этого периода сна. Мой код в настоящее время использует/зависит от блокировки чтения в данный момент; чтобы выполнить циклическое неблокирующее чтение в качестве очистителя, мне нужно было бы установить мои сокеты между неблокирующим и блокирующим... верно? Потому что я не хочу и не могу позволить моему сетевому прослушивателю постоянно опрашивать сокет. - person Cihan Keser; 20.02.2010
comment
У вас есть такая команда, как select (linux.die.net/man/2/select) в наличии? Если вы это сделаете, вы можете использовать select, чтобы определить, доступен ли какой-либо ввод, и вызывать функцию блокировки чтения только тогда, когда вам это нужно. Если у вас есть такая функция, я бы порекомендовал использовать select и неблокирующий ввод-вывод (в любом случае блокирующие функции чтения иногда реализуются внутри как select + неблокирующее чтение). Это даст вам больше гибкости и контроля, чем блокировка ввода-вывода. - person bta; 20.02.2010
comment
Если вы хотите игнорировать ввод-вывод только во время периода ожидания, установите переменную условия, когда период ожидания начинается, и очищайте ее, когда он заканчивается. Ваш поток ввода/вывода может проверять переменную condvar всякий раз, когда возвращается блокирующее чтение, и если переменная задана, она может отбросить данные и дождаться следующего ввода. Чтобы сделать это, используя предоставленный вами код, вы должны создать новый поток, который будет простым циклом ввода-вывода, а там, где вы вызвали recvfrom, вы вместо этого вызовете функцию, которая извлечет данные из цикла ввода-вывода (сохраните их в внутренняя очередь, например). - person bta; 20.02.2010
comment
Если вы хотите игнорировать ввод-вывод только во время периода сна... Точно! Но; дело в том, что мой период сна и поток ввода-вывода - это одни и те же потоки (см.: networkListenerFunction в OP). Хотя разделить их на 2 потока и реализовать свою идею можно; Мне любопытно, как это соотносится с идеей «уничтожить-сокет-в-endOfWhile-воссоздать-в-startOfWhile» из ответа Николая Н. Фетисова... - person Cihan Keser; 20.02.2010

Сокет имеет один буфер приема внутри стека TCP/IP. По сути, это FIFO полученных дейтаграмм. Однако TCP и UDP обрабатывают эту очередь по-разному. Когда вы вызываете recv(2) для сокета UDP, вы удаляете из очереди одну дейтаграмму из этого буфера. TCP упорядочивает дейтаграммы в поток байтов в соответствии с порядковыми номерами. Когда приемный буфер переполняется, дейтаграмма отбрасывается стеком. TCP пытается повторно отправить в этом случае. UDP нет. Для приемного буфера нет явной функции «сброса», кроме чтения сокета или его закрытия.

Редактировать:

В вашем приложении присутствует состояние гонки, и похоже, что вы пытаетесь решить его с помощью неправильного инструмента (стека TCP/IP). Что я думаю вам следует сделать, так это определить чистый конечный автомат для приложения. Обрабатывайте события, которые имеют смысл в текущем состоянии, игнорируйте события, которые не имеют смысла.

Еще одна вещь, на которую следует обратить внимание, — это использование многоадресной рассылки вместо широковещательной. Это немного сложнее, но у вас будет больше контроля над «подписками», присоединяясь к группам многоадресной рассылки или выходя из них.

person Nikolai Fetissov    schedule 19.02.2010
comment
Тогда было бы неплохо закрыть/уничтожить сокет в конце цикла while и заново создать/связать его в начале цикла? Кажется, слишком много работы для теоретически бесконечного цикла для машины с 200-мегагерцовым процессором... или не так? - person Cihan Keser; 20.02.2010
comment
Это зависит. Если вы закроете сокет, то, пока он закрыт, любые отправители на другом конце могут получить сообщения об ошибках ICMP для своих UDP-отправок на этот порт. Если это ничего не даст, дерзайте. - person T.E.D.; 20.02.2010
comment
Все машины отправляют сообщения на сетевой широковещательный адрес, поэтому; Я предполагаю, что они не будут получать эти сообщения об ошибках, поскольку адрес send_address специально не определен...? - person Cihan Keser; 20.02.2010
comment
Закрытие и повторное открытие порта не похоже на путь здесь. Когда вы закрываете порт, вы открываете возможность, что что-то еще может открыть его раньше вас. Кроме того, операции открытия/закрытия, скорее всего, потребуют больших накладных расходов. Не говоря уже о том, что это, вероятно, вызовет проблемы для любых удаленных устройств, подключенных к порту. После того, как вы открыли порт, не закрывайте его, пока полностью не закончите с ним работать. - person bta; 20.02.2010