Какова логика чтения, когда я вызываю функцию recvfrom() в C/C++

Я написал программу на C++ для создания сокета и привязки к этому сокету для получения пакетов ICMP/UDP. Код я написал следующим образом:

while(true){
   recvfrom(sockId, rePack, sizeof(rePack), 0, (struct sockaddr *)&raddr, (socklen_t *)&len);
   processPakcet(recv_size);
}

Итак, я использовал бесконечный цикл while для непрерывного получения сообщений, но меня беспокоили следующие два вопроса:

1, How long the message would be kept in the receiver queue or say in NIC queue?

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

2, How to prevent reading the duplicated messages?

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

Кроме того, я думаю, что модуль while(true) не очень хорош, кто-нибудь может дать мне хорошее предложение, пожалуйста. (Я слышал что-то вроде модуля опроса).


person Jack    schedule 12.12.2020    source источник


Ответы (1)


Во-первых, вы всегда должны проверять возвращаемое значение из recvfrom. Маловероятно, что recvfrom выйдет из строя, но если это произойдет (например, если вы позже реализуете обработку сигналов, это может привести к сбою с EINTR), вы будете обрабатывать неопределенные данные. Также, конечно же, возвращаемое значение говорит вам о размере полученного вами пакета.

Фактический ответ на вопрос 1 зависит от операционной системы. Однако большинство операционных систем будут буферизовать для вас некоторое количество пакетов. Обработчик прерывания ОС, который обрабатывает входящий пакет, никогда не будет копировать его непосредственно в буфер уровня вашего приложения, поэтому он всегда будет сначала помещаться в буфер ОС. ОС ранее отметила ваш интерес к нему (благодаря созданию сокета и привязке к нему вы выразили интерес), поэтому затем она поместит указатель на буфер в очередь, связанную с вашим сокетом.

Затем другая часть кода ОС (после завершения обработчика прерывания) скопирует данные из буфера ОС в память вашего приложения, освободит буфер ОС и вернется в вашу программу из системного вызова recvfrom. Если поступают дополнительные пакеты до или после того, как вы начали обрабатывать первый, они также будут помещены в очередь.

Эта очередь, конечно, не бесконечна. Вполне вероятно, что вы можете настроить, сколько пакетов (или сколько места в буфере) может быть зарезервировано либо на общесистемном уровне (подумайте о настройках типа sysctl в Linux), либо на уровне отдельных сокетов (setsockopt / ioctl).

Если при вызове recvfrom в сокете уже есть пакеты в очереди, обработчик системного вызова не будет блокировать ваш процесс, а просто скопирует из буфера ОС следующий пакет из очереди в ваш буфер , освободить буфер ОС и немедленно вернуться. Пока вы можете обрабатывать входящие пакеты примерно так же быстро, как они приходят, или быстрее, вы не должны терять ни одного. (Однако обратите внимание, что если другая система генерирует пакеты с очень высокой скоростью, вполне вероятно, что зарезервированная память ОС будет исчерпана в какой-то момент, после чего ОС просто отбросит пакеты, которые превышают ее ресурс. бронирование.)

Что касается вопроса 2, вы не получите повторяющихся сообщений (если только что-то выше по течению от вашей машины на самом деле не дублирует их). Как только поставленное в очередь сообщение копируется в ваш буфер, оно высвобождается, прежде чем вернуться к вам. Это сообщение исчезло навсегда.

(Обратите внимание, что возможно, что какой-то другой процесс также создал сокет, выражающий интерес к тем же пакетам. Этот процесс также получит копию данных пакета, которая обычно обрабатывается внутри операционной системы путем подсчета ссылок, а не путем фактического дублирования. буферы ОС, хотя эта деталь невидима для приложений. В любом случае, как только все заинтересованные процессы получат пакет, он будет отброшен.)

На самом деле в цикле while (true) нет ничего плохого; это очень распространенная структура управления для долго работающих программ серверного типа. Если вашей программе больше нечего делать в это время, while true разрешить ей блокировку в recvfrom будет самым простым и, следовательно, самым ясным способом ее реализации.

(Вы можете использовать вызов select(2) или poll(2) для ожидания. Это позволяет вам обрабатывать ожидание любого из нескольких файловых дескрипторов одновременно или периодически отключать тайм-аут и делать что-то еще, скажем, но опять же, если у вас нет ничего другого. вам, возможно, придется делать это тем временем, что вносит ненужные сложности.)

person Gil Hamilton    schedule 12.12.2020
comment
Обратите внимание, что возможно получение дубликатов сообщений UDP (т. е. UDP не гарантирует, что этого не произойдет), хотя это необычно, если только между отправителем и получателем нет избыточных сетевых путей. - person Jeremy Friesner; 13.12.2020