Recibir datos HTTP fragmentados con Winsock

Tengo problemas para leer algunos datos fragmentados de respuesta HTTP usando winsock. Envío bien una solicitud y recibo lo siguiente:

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 

usando winsock recv. En este punto, sin embargo, simplemente se bloquea. Tengo al oyente ejecutándose en un ciclo infinito, pero nunca se retoma nada.

Creo que es un problema de C ++, pero también podría estar relacionado con el hecho de que presiono la conexión a través de stunnel para concluir dentro de HTTPS. Tengo una aplicación de prueba que usa algunas librerías en C # que funciona perfectamente a través de stunnel. Estoy confundido sobre por qué mi bucle no está recibiendo los datos fragmentados de C ++ después del recv inicial.

Este es el ciclo en cuestión … se llama después de la respuesta bien resumida anterior …

 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; } } 

¿Algunas ideas?

Debes cambiar tu código de lectura. No puede leer datos chunked usando un búfer de longitud fija como lo está tratando de hacer. Los datos se envían en fragmentos de longitud variable, donde cada fragmento tiene un encabezado que especifica la longitud real del fragmento en bytes, y el fragmento final de los datos tiene una longitud de 0. Necesita leer los encabezados fragmentados para procesar los trozos adecuadamente Por favor lea RFC 2616 Sección 3.6.1 . Su lógica debe ser más como el siguiente pseudo-código:

 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; } } 

De hecho, no recibe fragmentos, pero el contenido está fragmentado. Debes dibujarte a ti mismo cómo podría verse el buffer que recibes. No es como si recibieras un pedazo en ese momento. A veces tiene algunos datos del fragmento anterior, la línea que indica el tamaño del nuevo fragmento, seguido de algunos datos de fragmentos. En otro momento, solo recibes solo un poco de datos de fragmentos. Otra vez, un poco de datos de fragmentos y una parte de la línea que indica el nuevo fragmento, etc., etc. Imagine los peores escenarios, esto no es fácil. Lea esto: http://www.jmarshall.com/easy/http/

Antes de que pueda usar el siguiente fragmento de código, reciba todos los encabezados hasta la línea vacía. Donde el contenido comienza en el buffer es nContentStart . El código utiliza algunas clases internas que no puedo compartir, pero debería tener la idea;) Por lo que he probado, funciona como se espera y no pierde memoria. Aunque como esto no es fácil, ¡no puedo estar completamente seguro!

  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 }