使用Winsock接收分块HTTP数据
Receiving Chunked HTTP Data With Winsock
我在使用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
使用winsock。然而,在这一点上,它只是挂着。我让监听器在一个无限循环中运行,但是没有任何东西被拾取。
我认为这是一个c++问题,但它也可能与我通过隧道推动连接以将其封装在HTTPS中的事实有关。我有一个测试应用程序,使用c#中的一些库,通过隧道完美地工作。我很困惑为什么我的循环在初始接收后没有接收到c++块数据。
这是正在讨论的循环…它在上面的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: %dn", err);
break;
}
}
任何想法?
您需要更改您的读取代码。你不能像你试图做的那样使用固定长度的缓冲区读取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;
response-version = extract response-version 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/")
{
boundary = extract boundary from Content-Type's "boundary" attribute;
recv() data into destination buffer until MIME termination boundary is reached;
}
else
{
recv() data into destination buffer until disconnected;
}
}
if (not disconnected)
{
if (response-version is "HTTP/1.1")
{
if (Connection header is "close")
close connection;
}
else
{
if (Connection header is not "keep-alive")
close connection;
}
}
check response-code for errors;
process destination buffer, per info in headers list;
确实没有收到分块,但是内容被分块了。你必须自己画一幅图,你收到的缓冲可能是什么样子的。你不可能一次只收到一大块。有时,您有前一个块的一些数据,这条线表示新块的大小,后面跟着一些块数据。还有一些时候,你只接收到一小块数据。另一次是一小块数据和一行的一部分,表示新的块,等等。想象一下最坏的情况,这并不容易。阅读: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
}
相关文章:
- 防止主数据类型C++的隐式转换
- 用于访问容器<T>数据成员的正确 API
- 在 C++/C 中使用 CURL 发出带有数据文件的 GET HTTP 请求
- 如何在C++中使用带有SFML的http reqest从节点.js服务器获取数据?
- 在 MySQL 中运行 HTTP 服务器以从客户端接收数据
- C++:如何通过 curl 调用使用 HTTP post 请求发送二进制数据(protobuf 数据)
- 使用 HTTP 方法 POST 或 PUT 上传数据
- 如何在 c++ 中在 http/https 端口上发送和接收数据?
- 如何从 HTTP 响应中删除垃圾数据
- 通过HTTP请求从站点检索数据(使用QT5.4的C++)
- 在浏览器中呈现数据之前,是否可以拦截http请求并修改数据(例如使用regex替换内容)?如果是,如何
- 响应标头和非HTTP数据的HTTP分离
- 接收recv数据,直到流结束(使用HTTP)
- http 请求在 & 符号后剪切数据 &
- (Qt或其他)拖放数据的mime类型的uri-list与协议http或ftp在Windows桌面或explorer.ex
- 使用libevent读取连续的HTTP流并在随机时间发送数据
- Libcurl HTTP post发送数据到服务器
- 使用Winsock接收分块HTTP数据
- Http post - 发送大数据 vc++ 时连接重置
- Boost ptime:如何以浏览器在http请求的header内发送的方式格式化数据