Получение фрагментированных данных HTTP с помощью Winsock - PullRequest
3 голосов
/ 29 августа 2011

У меня проблемы с чтением некоторых фрагментированных данных ответа 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.Я запутался, почему мой цикл не получает данные C ++ после первоначальной записи.

Это цикл, о котором идет речь ... он вызывается после ответа 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;
    }     

}

Есть идеи?

Ответы [ 2 ]

5 голосов
/ 29 августа 2011

Вам необходимо изменить код чтения. Вы не можете читать chunked данные, используя буфер фиксированной длины, как вы пытаетесь это сделать. Данные отправляются в чанках переменной длины, где каждый чанк имеет заголовок, который указывает фактическую длину чанка в байтах, а конечный чанк данных имеет длину 0. Вам нужно прочитать чанкованные заголовки, чтобы обрабатывать куски правильно. Пожалуйста, прочитайте 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;
    }
}
0 голосов
/ 30 августа 2011

Действительно, вы не получаете чанкинг, но контент чанкован.Вы должны нарисовать для себя, как может выглядеть любой полученный вами буфер.Это не так, как вы получаете один кусок в то время.Иногда у вас есть некоторые данные предыдущего чанка, строка, указывающая размер нового чанка, сопровождаемая некоторыми данными чанка.Как-то в другой раз вы просто получаете немного данных чанка.В другой раз немного данных чанка и часть строки, указывающая новый чанк и т. Д., И т. Д. Представьте себе худшие сценарии, это не легко.Прочитайте это: 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
    }
...