Получение Chunked HTTP-данных с помощью Winsock

У меня возникли проблемы с чтением некоторых fragmentированных данных ответа HTTP с помощью winsock. Я посылаю запрос штрафа и получаю следующее обратно:

HTTP/1.1 200 OK Server: LMAX/1.0 Content-Type: text/xml; charset=utf-8 Transfer-Encoding: chunked Date: Mon, 29 Aug 2011 16:22:19 GMT 

используя winsock recv. На данный момент, однако, это просто зависает. У меня есть слушатель, работающий в бесконечном цикле, но ничего не происходит.

Я думаю, что это проблема на C ++, но она также может быть связана с тем, что я нажимаю соединение через stunnel, чтобы обернуть его внутри HTTPS. У меня есть тестовое приложение, использующее некоторые библиотеки в C #, которые отлично работают через stunnel. Я смущен относительно того, почему мой цикл не получает данные Cunk chunked после первоначального recv.

Это цикл, о котором идет речь … он вызывается после ответа chunked ok выше …

 while(true) { recvBuf= (char*)calloc(DEFAULT_BUFLEN, sizeof(char)); iRes = recv(ConnectSocket, recvBuf, DEFAULT_BUFLEN, 0); cout << WSAGetLastError() << endl; cout << "Recv: " << recvBuf << endl; if (iRes==SOCKET_ERROR) { cout << recvBuf << endl; err = WSAGetLastError(); wprintf(L"WSARecv failed with error: %d\n", err); break; } } в while(true) { recvBuf= (char*)calloc(DEFAULT_BUFLEN, sizeof(char)); iRes = recv(ConnectSocket, recvBuf, DEFAULT_BUFLEN, 0); cout << WSAGetLastError() << endl; cout << "Recv: " << recvBuf << endl; if (iRes==SOCKET_ERROR) { cout << recvBuf << endl; err = WSAGetLastError(); wprintf(L"WSARecv failed with error: %d\n", err); break; } } 

Есть идеи?

    Вам нужно изменить свой код чтения. Вы не можете читать chunked данные, используя буфер фиксированной длины, как вы пытаетесь сделать. Данные отправляются в куски переменной длины, где каждый чанк имеет заголовок, который определяет фактическую длину fragmentа в байтах, а конечный fragment данных имеет длину 0. Вам нужно прочитать fragmentированные заголовки, чтобы правильно обработайте куски. Пожалуйста, прочтите RFC 2616 Раздел 3.6.1 . Ваша логика должна быть больше похожа на следующий псевдокод:

     send request; status = recv() a line of text until CRLF; parse status as needed; response-code = extract response-code from status; do { line = recv() a line of text until CRLF; if (line is blank) break; store line in headers list; } while (true); parse headers list as needed; if ((response-code is not in [1xx, 204, 304]) and (request was not "HEAD")) { if (Transfer-Encoding header is present and not "identity") { do { line = recv a line of text until CRLF; length = extract length from line; extensions = extract extensions from line; process extensions as needed; // optional if (length == 0) break; recv() length number of bytes into destination buffer; recv() and discard bytes until CRLF; } while (true); do { line = recv a line of text until CRLF; if (line is blank) break; store line in headers list as needed; } while (true); re-parse headers list as needed; } else if (Content-Length header is present) { recv() Content-Length number of bytes into destination buffer; } else if (Content-Type header starts with "multipart/") { recv() data into destination buffer until MIME terminator derived from the Content-Type's "boundary" attribute value is reached; } else { recv() data into destination buffer until disconnected; } } 

    На самом деле, вы не получаете chunked, но контент разделяется. Вы должны сами нарисовать себе, как выглядит любой буфер, который вы получаете. Это не похоже на то, что вы получаете один кусок в то время. Иногда у вас есть данные предыдущего fragmentа, строка, указывающая размер нового fragmentа, а затем некоторые данные куска. В какой-то другой момент вы просто получаете только немного данных куска. В другой раз немного данных куска и часть строки, указывающая новый кусок и т. Д. И т. Д. Представьте, что сценарии наихудшего случая, это непросто. Прочтите это: http://www.jmarshall.com/easy/http/

    Прежде чем вы сможете использовать следующий fragment кода, получите все заголовки до пустой строки. Когда содержимое начинается в буфере, nContentStart . В коде используются некоторые внутренние classы, которые я не могу разделить, но вы должны получить идею;) Насколько я проверял, он работает, как ожидалось, и не течет память. Хотя, поскольку это непросто, я не могу быть абсолютно уверен!

      if (bChunked) { int nOffset = nContentStart; int nChunkLen = 0; int nCopyLen; while (true) { if (nOffset >= nDataLen) {pData->SetSize(0); Close(); ASSERTRETURN(false);} // copy data of previous chunk to caller's buffer if (nChunkLen > 0) { nCopyLen = min(nChunkLen, nDataLen - nOffset); n = pData->GetSize(); pData->SetSize(n + nCopyLen); memcpy(pData->GetPtr() + n, buf.GetPtr() + nOffset, nCopyLen); nChunkLen -= nCopyLen; ASSERT(nChunkLen >= 0); nOffset += nCopyLen; if (nChunkLen == 0) nOffset += strlen(lpszLineBreak); ASSERT(nOffset <= nDataLen); } // when previous chunk is copied completely, process new chunk if (nChunkLen == 0 && nOffset < nDataLen) { // chunk length is specified on first line p1 = buf.GetPtr() + nOffset; p2 = strstr(p1, lpszLineBreak); while (!p2) // if we can't find the line break receive more data until we do { buf.SetSize(nDataLen + RECEIVE_BUFFER_SIZE + 1); nReceived = m_socket.Receive((BYTE*)buf.GetPtr() + nDataLen, RECEIVE_BUFFER_SIZE); if (nReceived == -1) {pData->SetSize(0); Close(); ASSERTRETURN(false);} // connection error if (nReceived == 0) {pData->SetSize(0); Close(); ASSERTRETURN(false);} // all data already received but did not find line break nDataLen += nReceived; buf[nDataLen] = 0; p1 = buf.GetPtr() + nOffset; // address of buffer likely changed p2 = strstr(p1, lpszLineBreak); } *p2 = 0; p2 += strlen(lpszLineBreak); p3 = strchr(p1, ';'); if (p3) *p3 = 0; if (sscanf(p1, "%X", &nChunkLen) != 1) {pData->SetSize(0); Close(); ASSERTRETURN(false);} if (nChunkLen < 0) {pData->SetSize(0); Close(); ASSERTRETURN(false);} if (nChunkLen == 0) break; // last chunk received // copy the following chunk data to caller's buffer nCopyLen = min(nChunkLen, buf.GetPtr() + nDataLen - p2); n = pData->GetSize(); pData->SetSize(n + nCopyLen); memcpy(pData->GetPtr() + n, p2, nCopyLen); nChunkLen -= nCopyLen; ASSERT(nChunkLen >= 0); nOffset = (p2 - buf.GetPtr()) + nCopyLen; if (nChunkLen == 0) nOffset += strlen(lpszLineBreak); if (nChunkLen == 0 && nOffset < nDataLen) continue; // a new chunk starts in this buffer at nOffset, no need to receive more data } // receive more data buf.SetSize(RECEIVE_BUFFER_SIZE + 1); nDataLen = m_socket.Receive((BYTE*)buf.GetPtr(), RECEIVE_BUFFER_SIZE); if (nDataLen == -1) {pData->SetSize(0); Close(); ASSERTRETURN(false);} if (nDataLen == 0) {pData->SetSize(0); Close(); ASSERTRETURN(false);} buf[nDataLen] = 0; nOffset = 0; } // TODO: receive optional footers and add them to m_headers } 
    Давайте будем гением компьютера.