2011-08-29 4 views
3

winsock을 사용하여 청크 분할 된 HTTP 응답 데이터를 읽는 데 문제가 있습니다. 나는 요청 벌금을 보내고 다시 다음을 얻을 :Winsock으로 청크 HTTP 데이터 수신

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 

윈속 RECV를 사용하여. 그러나이 시점에서 그것은 단지 매달린다. 나는 리스너를 무한 루프에서 실행시키지 만, 아무것도 포착하지 않는다.

나는 그것이 C++ 문제라고 생각하지만, HTTPS 내부에서 연결을 stunnel을 통해 연결한다는 사실과 관련 될 수도 있습니다. Stunnel을 통해 완벽하게 작동하는 C#의 일부 라이브러리를 사용하는 테스트 응용 프로그램이 있습니다. 나는 왜 내 루프가 초기 recv 후에 C++ 청크 데이터를받지 못하는지 혼란 스럽다.

은 ... 그것은

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

} 

어떤 아이디어 ... 위의 청크 확인 응답 후 문제의 루프라고한다?

+0

루프에서 할당하지 않도록 코드를 변경하고, 그렇지 않으면 한 번에 DEFAULT_BUFLEN 한 개씩 메모리가 누출됩니다. 또한 루프의 정지 조건은 무엇입니까? 이 'recv'에 도달하기 전에 데이터를 소비하고있을 가능성이 있습니까? – Hasturkun

+0

그래, 그게 기억을 새기고 있다는 것을 나는 이해하지만 나는 지금 당황하고있어. 쉽게 memset으로 전환 할 수 있습니다. 데이터가 도착하지 않는다는 것을 나타내는 모든 수신 후 인쇄를 수행합니다. –

+0

실수로 데이터를 소비하는지 확인하려면이 비트 앞에 코드를 게시하는 것이 좋습니다. 또한'recv'가 오류를 반환하면'recvBuf'가 수정되지 않으므로 인쇄는 무의미합니다. – Hasturkun

답변

5

독서 코드를 변경해야합니다. 당신이하려고하는 것처럼 고정 길이 버퍼를 사용하여 chunked 데이터를 읽을 수 없습니다. 데이터는 가변 길이 청크로 보내집니다. 각 청크는 청크의 실제 길이를 바이트 단위로 지정하는 헤더를 가지며 데이터의 마지막 청크는 길이가 0입니다. 청크 된 헤더를 읽어야합니다. 청크를 제대로 처리하십시오. RFC 2616 Section 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() data into destination buffer up to Content-Length number of bytes; 
    } 
    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; 
    } 
} 
0

실제로 청크 분할은 수신되지 않지만 내용은 청크 처리됩니다. 어떤 버퍼를 어떻게 보일지 직접 그린 그림을 그려야합니다. 그 때 한 청크를받는 것과 같지 않습니다. 때로는 이전 청크의 일부 데이터가 있습니다.이 선은 새 청크의 크기를 나타내며 그 다음에 청크 데이터가옵니다. 다른 때 당신은 방금 약간의 청크 데이터를받습니다. 또 다른 시간은 약간의 청크 데이터와 새로운 청크 등을 나타내는 라인의 일부입니다. 최악의 시나리오를 상상해보십시오. 이것은 쉽지 않습니다. 읽기 : http://www.jmarshall.com/easy/http/

다음 코드를 사용하기 전에 빈 줄까지 모든 헤더를 수신하십시오. 버퍼에서 내용이 시작되는 곳은 nContentStart입니다. 이 코드는 공유 할 수없는 몇 가지 사내 수업을 사용하지만 아이디어를 얻어야합니다;) 테스트 한만큼 예상대로 작동하고 메모리가 누출되지 않습니다. 이것은 쉬운 일이 아니기 때문에 완전히 확신 할 수는 없지만!

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 
    } 
관련 문제