【发布时间】:2014-11-30 14:11:50
【问题描述】:
我正在尝试使用 winsock 从我的网站下载文件。我遇到了无数的问题,现在我能够下载文件,但它已损坏。
它不适用于任何文件扩展名。文本和图片最终损坏,音频文件也是如此。对于二进制文件,我可以在执行“程序太大而无法放入内存”时看到此错误。
首先,我向服务器发送()一个 Head 请求以了解内容长度(要下载的文件的大小),然后我发送一个 Get 请求并将我接收到缓冲区中。 recv 完成后,我编写文件。
我尝试在这里编写一个简单的代码示例,我尝试了各种循环方法,但最后我仍然有一个损坏的文件写入磁盘。大小相同(服务器上的 50kb 文件,下载并写入磁盘的 50kb 文件)。 谢谢大家。
headrequest = "HEAD " + "/folder/file.asd" + " HTTP/1.1\r\nHost: " + "url.com" + "\r\n\r\n";
getrequest = "GET " + "/folder/file.asd" + " HTTP/1.1\r\nHost: " + "url.com" + "\r\n\r\n";
send(socket, headrequest, sizeof(headrequest), 0);
recv(socket, reply_buf_headrequest, sizeof(reply_buf_headrequest), 0);
//two functions to get the header end and "Content-Lenght" data from header
send(socket, getrequest, sizeof(getrequest), 0);
while(1)
{
recv(socket, recvbuff, sizeof(recvbuff), 0);
if (recv(socket, recvbuff, sizeof(recvbuff), 0) == 0)
break;
}
out.write(recvbuff, content_lenght); // also tried --> out.write(recvbuff + header_end, content_lenght) //same errors.
out.close();
我搞砸了缓冲区/位置以开始读/写或类似的东西。我认为使用 recvbuff + header_end 会起作用,因为它会从标题的末尾开始读取以获取文件。这令人困惑。 我希望一个善良的灵魂可以帮助我弄清楚如何处理这种情况并正确写入文件字节。 :)
编辑:
我认为我正在覆盖这样的数据。该死。 content_length 来自之前的 HEAD 请求,一个函数读取接收到的数据并找到“Content-Length”值,即 /folder/file.asd 的字节大小。 我无法在 Get 请求中得到它,所以我这样做了.. 它得到的文件大小是正确的。
所以,
while(1)
{
if (recv(socket, recvbuff, sizeof(recvbuff), 0) == 0)
break;
}
out.write(recvbuff, content_lenght);
out.close();
out.write 应该在循环之后还是在 while(1) 循环内?
感谢您的快速回复。 :)
我省略了错误检查部分以保持示例代码简短,抱歉。 头部和获取请求是字符,我也尝试过使用字符串,但最终没有使用 sizeof() 。直到明天我才能访问真正的代码,所以我尝试在家里使用类似的 sn-p 修复它。可能有一些错别字..
编辑 2: 使用一个小 exe 进行测试,该 exe 使用比文件大的缓冲区生成消息框即时消息,并且:
ofstream out("test.exe", ios::binary);
现在使用这个循环:
int res; // return code to monitor transfer
do {
res = recv(socket, recvbuff, sizeof(recvbuff), 0); // look at return code
if (res > 0) // if bytes received
out.write(recvbuff, res ); // write them
} while (res>0); // loop as long as we receive something
if (res==SOCKET_ERROR)
cerr << "Error: " << WSAGetLastError() << endl;
执行时仍然出现“程序太大而无法放入内存”错误..
【问题讨论】:
-
headrequest 和 getrequest 的数据类型是什么?如果它们是 std::string 或类似的字符串类,则 sizeof(...) 不会返回字符串长度。
-
您从不检查 send() 或 recv() 返回的值,因此您不知道实际发送了多少字节,也不知道实际将多少字节放入 recvbuff。它可能比您请求的字节数少,在这种情况下,您的缓冲区将不会被完全填充。
-
除了发布的解决方案之外,请确保您以二进制模式打开输出文件,否则将翻译换行符。
-
您的编辑仍然无法工作:recv() 将始终在缓冲区的开头写入它收到的内容。在循环结束时,您不断覆盖缓冲区,并且缓冲区仅包含最后几个字节。只有这样你才写缓冲区,但是对于文件的全长(可能会超出缓冲区大小)所以你会写很多未初始化的数据!
-
在发送
GET之前,您不需要使用HEAD来获取文件大小。 HTTP 请求/响应是自包含的消息。对GET的响应将告诉您如何在下载文件时确定文件的大小,无论是通过Content-Length标头、Transfer-Encoding: chunked标头等。 HTTP比你认为的要复杂得多(而且你的套接字读/写代码通常是完全错误的),所以你最好使用 WinInet/WinHTTP 或像 libcurl 这样的第三方库来处理所有这些细节给你。
标签: c++ windows http visual-c++ winsock