Winsock: функция recv - получение данных
Подскажите пожалуйста, в функции recv непонятен один момент:
Возвращаемое значение Если ошибок не происходит, recv возвращает количество полученных байтов, и буфер, на который указывает параметр buf, будет содержать эти полученные данные. Если соединение было корректно закрыто, возвращаемое значение равно нулю.
Вроде все понятно, но:
std::string my_string_buff;
my_string_buff.resize(100);
int my_recv = recv(my_socket, &my_string_buff[0], my_string_buff.size(), 0);
Предположим я выделил буфер для приема из 100 байт и предположим сервер мне выслал сообщение равное именно 100 байтам, то есть я получил полностью все данные от сервера, НО так как я заранее размер получаемых данных не знаю, мне нужно вызвать recv еще раз и так как данные от севера уже получены, то второй вызов вернет 0, и по описанию - это будет означать, что сервер закрыл сокет, но это же не обязательно так! Если я общаюсь по http и попросил keep-alive, то сокет будет открыт, но функция recv все равно вернет 0, так как данных на данный момент нет.
И вот непонятно, как обрабатывать этот момент.
Ответы (2 шт):
Неа, вы поняли неправильно. Если вы считали сто байт, а потом попытаетесь читать еще раз, вы получите не ноль - просто ваша программа заблокируется на функции recv, и будет стоять до тех пор, пока не получит хотя бы байт, или сервер со своей стороны не закроет соединение.
У вас есть два-с половиной варианта управлять этой ситуацией - сделать сокет асинхронным, и тогда вы получите отрицательное значение, а в errono будет что-то типа EAGAIN (EWOULDBLOCK) - так система вам намекнет, что данных для вас у нее нет.
Половина варианта, доступная на линуксе - сделать не весь сокет неблокирующим, а только данную операцию, подставив флаг MSG_NONBLOCK
И еще вариант - перед чтением проверять, что в сокет что-то пришло. Это всякие poll, select и их вариации.
то второй вызов вернет 0, и по описанию - это будет означать, что сервер закрыл сокет, но это же не обязательно так!
Это не так. По умолчанию recv() будет ждать пока данные не появятся. Также он может вернуть -1, если сокет находится в неблокирующем режиме и данные ещё не пришли или вызов будет прерван сигналом с установкой соответствующий ошибки EAGAIN или EINTR.
Возврат нуля для tcp-сокета в невырожденной ситуации — это надёжный признак закрытия соединения второй стороной.
ЗЫ: Также стоит помнить, что с прикладной точки зрения TCP представляет из себя поток байт. Разделение его на сообщения следует делать по средствам протоколов более высоких уровней. Как именно — уже писал здесь.