Насколько большим должен быть мой буфер recv при вызове recv в библиотеке сокетов - PullRequest
122 голосов
/ 19 мая 2010

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

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

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

Ответы [ 6 ]

219 голосов
/ 19 мая 2010

Ответы на эти вопросы различаются в зависимости от того, используете ли вы потоковый сокет (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(), то есть).

15 голосов
/ 19 мая 2010

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

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

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

15 голосов
/ 19 мая 2010

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

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

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

recv(socket, recv_buffer + idx, recv_buffer_size - idx, 0);
4 голосов
/ 09 августа 2016

Для сокета 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);

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

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

1 голос
/ 19 мая 2010

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

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

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

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

0 голосов
/ 19 мая 2010

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

...