Завершение цикла recv(), когда вся информация читается с помощью Winsock

У меня возникла проблема в цикле recv() для winsock. Я пытаюсь завершить цикл, когда iResult==0, однако цикл заканчивается только тогда, когда закрывается сокет. Кажется, он зависает на самом последнем recv(), где iResult будет равен 0. Итак, есть идеи о том, как эффективно завершить цикл? Моя конечная цель (будь то iResult == 0 или нет; возможно, я делаю это неправильно) — остановить цикл, когда вся отправленная информация будет прочитана. Вот петля.

    do
    {
        iResult = recv(socket, recvbuf, BUFLEN-1, 0);
        if(iResult > 0){
            // Null byte :)
            // Remove that garbage >:(
            recvbuf[iResult] = '\0';
            printf("Recvbuf: %s\n\n\niResult: %d\n",recvbuf,iResult);
            continue; // working properly
        }
        else if(iResult == 0)
            // Connection closed properly
            break;
        else
        {
            printf("ERROR! %ld",WSAGetLastError());
            break;
        }
    } while(iResult > 0);

Как я уже сказал, я получаю все данные, я просто не могу выйти из цикла. Следующим шагом будет запись данных обратно на сервер, но они зависают здесь до истечения времени ожидания ping. Сокет — SOCK_STREAM, а BUFLEN определяется как 0x200.

Спасибо


person RageD    schedule 27.07.2010    source источник


Ответы (2)


По умолчанию вместо возврата 0 recv блокирует, если есть нет данных для получения :

Если в сокете нет доступных входящих данных, вызов recv блокируется и ожидает поступления данных в соответствии с правилами блокировки, определенными для WSARecv, с неустановленным флагом MSG_PARTIAL, если только сокет не является неблокирующим. В этом случае возвращается значение SOCKET_ERROR с кодом ошибки, установленным на WSAEWOULDBLOCK. Функции select, WSAAsyncSelect или WSAEventSelect можно использовать для определения времени поступления дополнительных данных.

Вы можете использовать ioctlsocket, чтобы поместить в неблокирующем режиме:

u_long iMode = 1;
ioctlsocket(socket, FIONBIO, &iMode);

РЕДАКТИРОВАТЬ: вот предложение setsockopt, которое я сделал в более ранней версии, а затем удалил (см. комментарии):

Вы можете использовать функцию setsockopt. с опцией SO_RCVTIMEO, чтобы установить время ожидания сокета на recv, если данные недоступны.

person Dan Breslau    schedule 27.07.2010
comment
Я хочу, чтобы он блокировался, чтобы я мог читать данные, но ваше предложение setsockopt() сработало отлично! Большое спасибо! - person RageD; 27.07.2010
comment
@ Деннис М.: Спасибо. Я удалил предложение setsockopt в пользу использования вместо него ioctlsocket, но любой из них должен работать. Я отредактировал setsockopt обратно в ответ. - person Dan Breslau; 27.07.2010

Когда вы разрабатываете механизм связи TCP, вы должны определить границы сообщений. (часто \r\n). Сам по себе tcp ничего не знает о границах; вы должны сделать это сами. Вызов recv() не всегда может возвращаться на границе сообщения. Один send() может быть разделен на несколько recv() на другом конце.

person seand    schedule 27.07.2010
comment
И наоборот, несколько send() также могут привести к одному recv(). - person Remy Lebeau; 28.07.2010
comment
хорошее пояснение - между send() и recv() может не быть однозначного соответствия. Однако в большинстве случаев кажется, что один send() вызывает один recv()!. Иногда это сбивает с толку людей, когда их приложение не работает под большой нагрузкой. - person seand; 30.07.2010