Насколько большим должен быть мой буфер recv при вызове recv в библиотеке сокетов


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

char recv_buffer[3000];
recv(socket, recv_buffer, 3000, 0);
  1. как мне решить, какой recv_buffer? Я использую 3000, но это условно.
  2. что произойдет, если recv() получает пакет больше, чем мои буфера?
  3. как я могу знать, если я получил все сообщение без вызова recv снова и есть его ждать вечно, когда нет ничего, чтобы быть получил?
  4. есть ли способ, которым я могу сделать буфер не иметь фиксированного количества пространства, так что я могу продолжать добавлять к нему, не боясь бежать из пространства? может быть, с помощью strcat для объединения последних recv() ответ в буфер?

Я знаю, что много вопросов в одном, но я был бы весьма признателен за любые ответы.

6 112

6 ответов:

ответы на эти вопросы зависят от того, используете ли вы потоковый сокет (SOCK_STREAM) или сокет дейтаграммы (SOCK_DGRAM) - в TCP / IP первый соответствует TCP, а второй-UDP.

как вы знаете, как большой, чтобы сделать буфер передается в recv()?

  • SOCK_STREAM: Это действительно не имеет большого значения. Если ваш протокол является транзакционным / интерактивным, просто выберите размер, который может содержать самый большой отдельное сообщение / команда, которую вы разумно ожидаете (3000, вероятно, в порядке). Если ваш протокол передает массовые данные, то более крупные буферы могут быть более эффективными - хорошее эмпирическое правило примерно такое же, как размер буфера ядра в сокете (часто что-то около 256 КБ).

  • SOCK_DGRAM: используйте буфер, достаточно большой для хранения самого большого пакета, который когда-либо отправлял ваш протокол уровня приложения. Если вы используете UDP, то в целом ваш уровень приложения протокол не должен отправлять пакеты размером более 1400 байт, потому что они, безусловно, должны быть фрагментированы и повторно собраны.

что произойдет, если recv получает пакет больше, чем буфер?

  • SOCK_STREAM: вопрос действительно не имеет смысла, как положено, потому что потоковые сокеты не имеют понятия пакетов - они просто непрерывный поток байтов. Если доступно для чтения больше байтов, чем ваш буфер имеет место для, то они будут поставлены в очередь ОС и доступны для вашего следующего вызова recv.

  • SOCK_DGRAM: лишние байты отбрасываются.

как я могу знать, если я получил все сообщение?

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

  • SOCK_DGRAM: один recv вызов всегда возвращает одну дейтаграмму.

есть ли способ я могу сделать буфер не имеет фиксированного объема пространства, так что я могу продолжать добавлять к нему, не опасаясь исчерпания пространства?

нет. Однако вы можете попробовать изменить размер буфера с помощью realloc() (если он был изначально выделен malloc() или calloc(), то есть).

для потоковых протоколов, таких как TCP, вы можете в значительной степени установить буфер любого размера. Тем не менее, общие значения, которые являются степенями 2, такие как 4096 или 8192 рекомендуется.

Если есть больше данных, чем ваш буфер, он просто будет сохранен в ядре для вашего следующего вызова recv.

Да, вы можете продолжать увеличивать свой буфер. Вы можете сделать recv в середине буфера, начиная со смещения idx, вы могли бы сделать:

recv(socket, recv_buffer + idx, recv_buffer_size - idx, 0);

если у вас SOCK_STREAM гнездо recv просто получает "до первых 3000 байт" из потока. Нет четкого руководства о том, как Большой сделать буфер: единственный раз, когда вы знаете, насколько большой поток, это когда все это сделано;-).

если у вас SOCK_DGRAM сокет, и дейтаграмма больше, чем буфер,recv заполняет буфер первой частью дейтаграммы, возвращает -1 и устанавливает errno в EMSGSIZE. К сожалению, если протокол UDP, это означает, что остальные дейтаграмма потеряна-часть того, почему UDP называется неблагонадежных протокол (я знаю, что существуют надежные протоколы датаграмм, но они не очень популярны-я не мог назвать один из семейства TCP/IP, несмотря на то, что хорошо знал последний; -).

чтобы динамически увеличить буфер, выделите его изначально с помощью malloc и использовать realloc по мере необходимости. Но это не поможет вам с recv из источника UDP, увы.

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

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

#include <sys/ioctl.h>
int size;
ioctl(sockfd, FIONREAD, &size);

в качестве альтернативы вы можете использовать MSG_PEEK и MSG_TRUNC флаги recv() вызов для получения ожидающего размера дейтаграммы.

ssize_t size = recv(sockfd, buf, len, MSG_PEEK | MSG_TRUNC);

затем вы можете просто malloc() буфер и получение дейтаграммы.

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

по данным RFC 768, размер пакета (включая заголовок) для UDP может варьироваться от 8 до 65 515 байт. Таким образом, отказоустойчивый размер для входящего буфера будет 65 507bytes (~64KB)

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

каков оптимальный размер пакета UDP для максимальной пропускной способности?
каков самый большой Безопасный размер пакета UDP в Интернете

16кб о праве; если вы используете локальные сети гигабита, то каждый пакет смог быть 9кб в размере.