Ответы на эти вопросы различаются в зависимости от того, используете ли вы сокет потока ( 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()
, то есть).
recv
в буфер после частичного сообщения. Вы не должны использоватьstrstr()
его для необработанного буфера, заполненногоrecv()
- нет гарантии, что он содержит нулевой терминатор, поэтому это может вызватьstrstr()
сбой.recv()
нужно записать больше байтов, чем осталось места.Для потоковых протоколов, таких как TCP, вы можете установить любой размер буфера. Тем не менее, рекомендуются общие значения, представляющие собой степени двойки, такие как 4096 или 8192.
Если данных больше, чем ваш буфер, они просто сохранятся в ядре для вашего следующего вызова
recv
.Да, вы можете продолжать увеличивать свой буфер. Вы можете выполнить recv в середине буфера, начиная со смещения
idx
, вы должны:источник
Если у вас есть
SOCK_STREAM
сокет, онrecv
просто получает «до первых 3000 байтов» из потока. Нет четких указаний относительно размера буфера: единственный раз, когда вы знаете, насколько велик поток, - это когда все готово ;-).Если у вас есть
SOCK_DGRAM
сокет, а дейтаграмма больше, чем буфер,recv
заполняет буфер первой частью дейтаграммы, возвращает -1 и устанавливает для errno значение EMSGSIZE. К сожалению, если протоколом является UDP, это означает, что остальная часть дейтаграммы потеряна - отчасти поэтому UDP называется ненадежным протоколом (я знаю, что существуют надежные протоколы дейтаграмм, но они не очень популярны - я не мог назовите один из семейства TCP / IP, несмотря на то, что он хорошо знает последнее ;-).Чтобы увеличить буфер динамически, сначала выделите его
malloc
и используйте поrealloc
мере необходимости. Но этоrecv
, увы, не поможет вам с источником UDP.источник
Для
SOCK_STREAM
сокета размер буфера на самом деле не имеет значения, потому что вы просто извлекаете некоторые из ожидающих байтов и можете получить больше в следующем вызове. Просто выберите любой размер буфера, который вы можете себе позволить.Для
SOCK_DGRAM
сокета вы получите подходящую часть ожидающего сообщения, а остальное будет отброшено. Вы можете получить размер ожидающей датаграммы с помощью следующего ioctl:В качестве альтернативы вы можете использовать
MSG_PEEK
иMSG_TRUNC
флагиrecv()
вызова , чтобы получить размер датаграммы ожидания.Вам нужно
MSG_PEEK
просмотреть (не получить) ожидающее сообщение - recv возвращает реальный, а не усеченный размер; и вам не нужноMSG_TRUNC
переполнять текущий буфер.Затем вы можете просто
malloc(size)
использовать настоящий буфер иrecv()
дейтаграмму.источник
malloc()
используем буфер размером 64 КБ, тогдаMSG_TRUNC
это не нужно?SOCK_DGRAM
есть не только UDP.На ваш вопрос нет однозначного ответа, потому что технология всегда зависит от реализации. Я предполагаю, что вы общаетесь по UDP, потому что размер входящего буфера не создает проблем для TCP-связи.
Согласно RFC 768 , размер пакета (включая заголовок) для UDP может составлять от 8 до 65 515 байт. Таким образом, безотказный размер входящего буфера составляет 65 507 байт (~ 64 КБ).
Однако не все большие пакеты могут быть правильно маршрутизированы сетевыми устройствами, обратитесь к существующему обсуждению для получения дополнительной информации:
Каков оптимальный размер пакета UDP для максимальной пропускной способности?
Каков самый большой размер безопасного UDP-пакета в Интернете
источник
16kb - это примерно правильно; если вы используете гигабитный Ethernet, размер каждого пакета может составлять 9 КБ.
источник