Насколько большим должен быть мой буфер 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 ответов


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

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

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

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

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

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

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

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

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

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

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

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


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

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

Да, вы можете продолжать увеличивать свой буфер. Вы можете сделать recv в середине буфера, начиная с offset 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 в Интернете


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