Путаница с возвращаемыми значениями UDP/IP и sendto/recvfrom

Я впервые работаю с UDP-сокетами на C++ и не уверен, что понимаю, как они работают. Я знаю, что sendto/recvfrom и send/recv обычно возвращают количество фактически отправленных или полученных байтов. Я слышал, что это значение может быть сколь угодно малым (но не менее 1) и зависит от того, сколько данных находится в буфере сокета (при чтении) или сколько свободного места осталось в буфере (при записи).

Если sendto и recvfrom гарантируют только то, что за один раз будет отправлен или получен 1 байт, и дейтаграммы могут быть получены не по порядку, как любой протокол UDP может оставаться согласованным? Разве это не означает, что байты в сообщении могут быть произвольно перемешаны, когда я их получаю? Есть ли способ гарантировать, что сообщение будет отправлено или получено сразу?


person Jay Conrod    schedule 23.04.2009    source источник


Ответы (4)


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

Но поскольку вы можете реализовать весь TCP с помощью UDP, это должно быть возможно.

обычно то, что вы делаете с UDP, — это создание небольших дискретных пакетов.

Метафорически представьте UDP как отправку открыток, а TCP как телефонный звонок. Когда вы отправляете открытку, у вас нет гарантии доставки, поэтому вам нужно сделать что-то вроде подтверждения. Когда вы звоните по телефону, вы знаете, что связь существует, и сразу же слышите ответы.

person Charlie Martin    schedule 23.04.2009

Нет. С помощью sendto вы отправляете пакеты, которые могут содержать до одного байта. Если вы отправляете 10 байтов как один вызов sendto, эти 10 байтов будут отправлены в один пакет, который будет получен когерентно, как вы и ожидали.

Конечно, если вы решите отправить эти 10 байтов один за другим, каждый из них с вызовом sendto, то на самом деле вы отправите и получите 10 разных пакетов (каждый из которых содержит 1 байт), и они могут быть в произвольном порядке.

Это похоже на отправку книги по почте. Вы можете упаковать книгу целиком в одну коробку или вырвать каждую страницу и отправить ее отдельным письмом. В первом случае упаковка более объемная, но вы получаете книгу как единое целое. В последнем каждая упаковка очень легкая, но удачи в чтении;)

person Stefano Borini    schedule 23.04.2009

На самом деле вы можете отправить дейтаграмму UDP длиной 0 байт. Все, что отправляется, — это заголовки IP и UDP. UDP recvfrom() на другой стороне вернется с длиной 0. В отличие от TCP это не означает, что одноранговый узел закрыл соединение, потому что с UDP нет «соединения».

person Duck    schedule 23.04.2009

У меня есть клиентская программа, которая использует выбор блокировки (параметр тайм-аута NULL) в потоке, предназначенном для ожидания входящих данных в сокете UDP. Несмотря на то, что он блокируется, выбор иногда возвращался с указанием, что единственный дескриптор чтения был «готов». Последующий recvfrom вернул 0.

После некоторых экспериментов я обнаружил, что по крайней мере в Windows отправка UDP-пакета на порт хоста, который не ожидает этого, может привести к последующему получению 0 байтов. Я подозреваю, что с другого конца может прийти какое-то уведомление об отказе. Теперь я использую это как напоминание о том, что я забыл запустить процесс на сервере, который ищет входящий трафик клиента.

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

person Tom Bates    schedule 21.03.2012