2010-12-06 1 views
9

Я использую сокеты berkeley и TCP (SOCK_STREAM сокеты).О recv и буфере чтения - C гнезда Berkeley

Процесс:

  1. подключиться к удаленному адресу.
  2. Я посылаю ему сообщение.
  3. Получаю сообщение от этого.

Представьте себе, я использую следующий буфер:

char recv_buffer[3000]; 
recv(socket, recv_buffer, 3000, 0); 

Вопросы:

  • Как я могу знать, если после вызова ПРИЕМА первого раза буфер чтения пуст или нет? Если он не пуст, мне придется снова вызвать recv, но если я это сделаю, когда он будет пуст, мне придется блокировать его на много времени.
  • Как узнать, сколько байтов я прочитал в recv_buffer? Я не могу использовать strlen, потому что получаемое сообщение может содержать нулевые байты.

Спасибо.

ответ

12

Как я могу знать, если после вызова RECV первого раза буфер чтения пуст или нет? Если он не пуст, мне придется снова вызвать recv, но если я сделаю это, когда будет пустым, я бы его блокировал на много времени.

Вы можете использовать систему select или poll вызовы вместе с дескриптором сокета, чтобы сказать, если есть данные, ожидая, чтобы быть прочитана из сокета.

Однако, как правило, должен быть согласованный протокол, которым следуют как отправитель, так и получатель, чтобы обе стороны знали, сколько данных необходимо передать. Например, возможно, отправитель сначала отправляет 2-байтовое целое число, указывающее количество отправленных байтов. Затем приемник сначала считывает это 2-байтовое целое число, так что он знает, сколько еще байтов считывается из сокета.

Независимо от того, как указал Тони ниже, надежное приложение должно использовать комбинацию длины информации в заголовке в сочетании с опросом сокета для дополнительных данных перед каждым вызовом до recv (или с использованием неблокирующего сокета) , Это предотвратит блокировку вашего приложения в случае, если, например, вы знаете (из заголовка), что все еще должно оставаться 100 байтов для чтения, но одноранговый узел не может отправить данные по любой причине (возможно, одноранговый компьютер был неожиданно отключается), тем самым вызывая блокировку вашего recv.

Как узнать, сколько байтов у меня есть , записанное в recv_buffer? Я не могу использовать strlen, потому что сообщение, которое я получаю , может содержать нулевые байты.

Системный вызов recv будет возвращать количество прочитанных байтов или -1, если произошла ошибка.

От человека странице для RECV (2):

[RECV] возвращает количество байтов, полученных , или -1, если произошла ошибка. Возвращаемое значение будет 0, когда сверстень выполнил упорядоченное завершение .

+0

Какова актуальность справочной страницы `read (2)` `recv (2)`? Говорят похожие вещи, но цитирование соответствующей страницы было бы лучше. – 2010-12-06 01:46:14

+2

@Jonathan, когда тип дескриптора является сокетом, `read` совпадает с` recv`, кроме `recv` разрешает дополнительный параметр flags. Но я отредактировал свой ответ, чтобы использовать `recv`, чтобы избежать путаницы. – 2010-12-06 01:53:59

+0

Просто nitpick re тонкая, предположительно непреднамеренная импликация: «select/poll/how message-length в заголовке» ложно предполагает, что такие заголовки разрешают проблему блокировки, где - как select/poll, неблокирующие сокеты или потоки должны использоваться в сочетании с заголовком длины сообщения или данными дозорного устройства. – 2010-12-06 02:16:36

0
  1. Если recv() возвращает меньше, чем 3000 байт, то можно предположить, что буфер чтения был пуст. Если он возвращает 3000 байт в вашем 3000-байтовом буфере, тогда вам лучше знать, продолжать ли это. Большинство протоколов включают некоторые изменения в TLV - тип, длину, значение. Каждое сообщение содержит индикатор типа сообщения, некоторую длину (возможно, подразумеваемый типом, если длина фиксирована) и значение. Если при чтении данных, которые вы получили, вы обнаружите, что последний блок неполный, вы можете предположить, что еще нужно прочитать. Вы также можете сделать сокет неблокирующим сокетом; то recv() не будет работать с EAGAIN или EWOULDBLOCK, если нет данных, считанных для чтения.

  2. Функция recv() возвращает количество прочитанных байтов.

2

Как я могу знать, если после вызова ПРИЕМА первого раза буфер чтения пуст или нет?

Даже в первый раз (после принятия клиента) recv может блокироваться и терпеть неудачу, если клиентское соединение было потеряно. Вы должны:

  • использование select или poll (BSD сокеты) или некоторый OS специфического эквивалента, который может сказать вам, есть ли доступны на определенных сокетах дескрипторов данные (а также условия исключения, и буферное пространства вы можете написать больше продукции на)
  • вы можете установить сокет быть блокирующим, так что recv будет возвращать только то, что не сразу доступно (возможно, ничего)
  • вы можете создать поток, который вы можете себе позволить, чтобы заблокировать recv -ный данные , зная, что другие потоки будут выполнять другую работу, которую вы хотите продолжить, с помощью

Как узнать, сколько байтов я прочитал в recv_buffer? Я не могу использовать strlen, потому что получаемое сообщение может содержать нулевые байты.

recv() возвращает количество прочитанных байтов или -1 при ошибке.

Обратите внимание, что протокол TCP представляет собой протокол байта, что означает, что вы гарантированно сможете читать и писать байты из него в правильном порядке, но границы сообщений не гарантируются. Таким образом, даже если отправитель сделал большую запись в своем гнезде, он может быть фрагментирован по маршруту и ​​поступать в несколько меньших блоков, или несколько меньших send()/write() s могут быть объединены и извлечены одним recv()/read().

По этой причине убедитесь, что вы звоните recv, пока не получите все необходимые данные (т. Е. Полное логическое сообщение, которое вы можете обработать) или ошибку. Вы должны быть готовы/способны обрабатывать получение части/всех последующих send s от вашего клиента (если у вас нет протокола, по которому каждая сторона отправляет только после получения полного сообщения от другого и не использует заголовки с длинами сообщений). Обратите внимание, что выполнение recvs для заголовка сообщения (с длиной), то тело может привести к намного большему количеству вызовов до recv(), что может отрицательно сказаться на производительности.

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

0

ioctl() с опцией FIONREAD указывает, сколько данных может быть прочитано в настоящий момент без блокировки.