Судя по комментариям выше, кажется, вы не понимаете, как recv
работает или как его предполагается использовать.
Вы действительно хотите позвонить recv
в цикле до тех пор, пока вы не узнаете, что ожидаемый объем данных был получен, или пока не получите результат «нулевое чтение байтов», что означает, что другой конец закрыл соединение.Всегда, без исключений.
Если вам нужно выполнять другие действия одновременно (вероятно, с серверным процессом!), То вам, вероятно, нужно сначала проверить готовность дескриптора с помощью poll
или epoll
.Это позволяет вам мультиплексировать сокеты по мере их готовности.
Причина, по которой вы хотите делать это таким образом, и ничем не отличающимся, заключается в том, что вы не знаете, как данные будут упакованы и как (или когда) пакеты будут приходить.Кроме того, recv
не дает никаких гарантий относительно количества прочитанных данных за один раз.Он будет предлагать то, что имеет в своих буферах, в тот момент, когда вы его называете, не больше и не меньше (он может блокировать, если ничего нет, но тогда вы все еще не будете иметь гарантии того, что какое-то конкретное количестводанные будут возвращены, когда они возобновятся, они все равно могут вернуть, например, 50 байтов!).
Даже если вы отправляете, скажем, всего 5000 байт, для TCP вполне допустимо разбивать это значение на 5 (или10 или 20) пакетов, и для recv
необходимо возвращать 500 (или 100, или 20, или 1) байтов за раз, каждый раз, когда вы вызываете его. Вот как это работает.
TCP гарантирует, что все, что вы отправите, в конечном итоге попадет на другой конец или выдаст ошибку.И это гарантирует, что все, что вы отправляете, приходит в порядок.Это не гарантирует многое другое.Прежде всего, это не гарантирует, что какой-либо конкретный объем данных будет готов в любой момент времени.
Вы должны быть к этому готовы, и единственный способ сделать это - повторно вызывать recv
.В противном случае вы всегда потеряете данные при некоторых обстоятельствах.
MSG_WAITALL
должен в принципе заставить работать так, как вы ожидаете, но это плохое поведение, и это не гарантируется.Если сокет (или другая структура в сетевом стеке) работает с мягким или жестким ограничением, он может не и, вероятно, не выполнит ваш запрос.Некоторые ограничения тоже неясны.Например, число для SO_RCVBUF
должно быть в два раза больше , чем то, что вы ожидаете получить в Linux, из-за подробностей реализации.
Правильное поведение серверного приложения должно никогда не зависит от допущений, таких как «он помещается в приемный буфер».В принципе, ваше приложение должно быть подготовлено для получения терабайтов данных с использованием 1-килобайтного приемного буфера и кусками по 1 байт за раз, если это необходимо.Больший приемный буфер сделает его более эффективным, но это все ... он все равно должен работать в любом случае.
Тот факт, что вы видите только неудачи выше некоторого "огромного" предела, - это просто удача (или, скорее,, невезение).Тот факт, что он, по-видимому, «работает» до этого предела, говорит о том, что вы делаете правильно, но это не так.Это несчастливое совпадение, что это работает.
РЕДАКТИРОВАТЬ:
Как указано в комментарии ниже, вот как это может выглядеть ( Код явно не проверен, будьте осторожны, emptor.)
std::vector<char> result;
int size;
char recv_buf[250];
for(;;)
{
if((size = recv(fd, recv_buf, sizeof(recv_buf), 0)) > 0)
{
for(unsigned int i = 0; i < size; ++i)
result.push_back(recv_buf[i]);
}
else if(size == 0)
{
if(result.size() < expected_size)
{
printf("premature close, expected %u, only got %u\n", expected_size, result.size());
}
else
{
do_something_with(result);
}
break;
}
else
{
perror("recv");
exit(1);
}
}
Это будет получать любой объем данных, который вы хотите (или до тех пор, пока operator new
не сгенерирует bad_alloc
после выделения вектора размером в несколько сотен мегабайт, но это другая история ...).
Если вы хотите обрабатывать несколько соединений, вам нужно добавить poll
или epoll
или kqueue
или аналогичные функции (или ... fork
), я оставлю этов качестве упражнения для читателя.