Обработка частичного возврата из recv() TCP в C

Я прочитал Beej Guide to Network Programming, чтобы получить доступ к TCP-соединениям. В одном из образцов клиентский код для простого клиента TCP-потока выглядит так:

if ((numbytes = recv(sockfd, buf, MAXDATASIZE-1, 0)) == -1) {
    perror("recv");
    exit(1);
}

buf[numbytes] = '\0';

printf("Client: received '%s'\n", buf);

close(sockfd);

Я установил буфер меньше, чем общее количество байтов, которые я отправляю. Я не совсем уверен, как я могу получить другие байты. Должен ли я перебирать recv(), пока не получаю '\0'?

* Примечание на стороне сервера. Я также реализую его функцию sendall(), поэтому она должна фактически отправлять все клиенту.

См. также 6.1. Простой сервер потоков в руководстве.

Ответ 1

Да, вам понадобится несколько вызовов recv(), пока у вас не будет всех данных.

Чтобы узнать, когда это происходит, использование статуса возврата из recv() не подходит - оно только сообщает вам, сколько байтов вы получили, а не сколько байтов доступно, поскольку некоторые из них все еще могут быть в пути.

Лучше, если полученные вами данные каким-то образом кодируют длину общих данных. Прочитайте столько данных, пока не узнаете, что такое длина, затем прочитайте, пока не получите данные length. Для этого возможны различные подходы; общий - сделать буфер достаточно большим, чтобы хранить все данные, как только вы знаете, что такое длина.

Другой подход заключается в использовании буферов фиксированного размера и всегда пытайтесь получить min(missing, bufsize), уменьшая missing после каждого recv().

Ответ 2

Первое, что вам нужно узнать при программировании TCP/IP: 1 write/send вызов может занять несколько вызовов recv для приема, а для нескольких вызовов для записи и отправки может потребоваться только один вызов recv для приема. И что-нибудь между ними.

Вам понадобится цикл, пока у вас не будет всех данных. Возвращаемое значение recv() сообщает, сколько данных вы получили. Если вы просто хотите получать все данные в TCP-соединении, вы можете зацикливаться до тех пор, пока recv() не вернет 0 - при условии, что другой конец закрывает TCP-соединение, когда он будет отправлен.

Если вы отправляете записи/строки/пакеты/команды или что-то подобное, вам нужно сделать свой собственный протокол через TCP, что может быть так же просто, как "команды разделены на \n".

Простым способом чтения/разбора такой команды будет чтение 1 байт за раз, создание буфера с полученными байтами и проверка байта \n каждый раз. Чтение 1 байт крайне неэффективно, поэтому вы должны читать более крупные фрагменты за раз.

Поскольку TCP ориентирован на поток и не обеспечивает границы записи/сообщения, он становится немного более сложным - вы бы должны recv кусок байта, проверить полученный буфер для байта \n, если он там - добавить байты к ранее принятым байтам и вывести это сообщение. Затем проверьте оставшуюся часть буфера после \n, которая может содержать другое целое сообщение или только начало другого сообщения.

Ответ 3

Да, вам нужно пройти через recv(), пока не получите '\0' или ошибка (отрицательное значение от recv) или 0 от recv(). Для первого варианта: только если этот ноль является частью вашего протокол (сервер отправляет его). Однако из вашего кода кажется, что нуль должен просто использовать буферный контент как C-строка (на стороне клиента).

Проверка возвращаемого значения 0 из recv: это означает, что соединение было закрыто (это может быть часть вашего протокола, что это происходит.)