2012-02-22 46 views
1

我想使用http协议GET来请求服务器发送给我一张图片。它编译并将要求图片,但服务器只会给我一小部分。用http下载图片只下载它的一小部分

我的代码是:

#include <iostream> 
#include <string> 
#include <Windows.h> 
#include <fstream> 

#include <stdlib.h> 
#include <winsock.h> 

#pragma comment(lib, "wsock32.lib") 

using namespace std; 

#define BUFFERSIZE 1024 
void die_with_error(char *errorMessage); 
void die_with_wserror(char *errorMessage); 

int main(int argc, char *argv[]) 
{ 
string request; 
string response; 
int resp_leng; 

char buffer[BUFFERSIZE]; 
struct sockaddr_in serveraddr; 
int sock; 

WSADATA wsaData; 
char *ipaddress = "210.125.167.240"; 
int port = 80; 

//http://cwc.ghc.ac.kr/skin/base/board/view_brit2.gif 
request+="GET /skin/base/board/view_brit2.gif HTTP/1.0\r\n"; 
request+="Host: cwc.ghc.ac.kr\r\n"; 
request+="\r\n"; 

//init winsock 
if (WSAStartup(MAKEWORD(2, 0), &wsaData) != 0) 
    die_with_wserror("WSAStartup() failed"); 

//open socket 
if ((sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) 
    die_with_wserror("socket() failed"); 

//connect 
memset(&serveraddr, 0, sizeof(serveraddr)); 
serveraddr.sin_family  = AF_INET; 
serveraddr.sin_addr.s_addr = inet_addr(ipaddress); 
serveraddr.sin_port  = htons((unsigned short) port); 
if (connect(sock, (struct sockaddr *) &serveraddr, sizeof(serveraddr)) < 0) 
    die_with_wserror("connect() failed"); 

//send request 
if (send(sock, request.c_str(), request.length(), 0) != request.length()) 
    die_with_wserror("send() sent a different number of bytes than expected"); 

//get response 
response = ""; 
resp_leng= BUFFERSIZE; 
while (resp_leng == BUFFERSIZE) 
{ 
    resp_leng= recv(sock, (char*)&buffer, BUFFERSIZE, 0); 
    if (resp_leng>0) 
     response+= string(buffer).substr(0,resp_leng); 
} 

ofstream myfile; 
myfile.open ("C:\\a\\example.gif"); 

//display response 
cout << response << endl; 

//get response 
response = ""; 
resp_leng= BUFFERSIZE; 
while (resp_leng == BUFFERSIZE) 
{ 
    resp_leng= recv(sock, (char*)&buffer, BUFFERSIZE, 0); 
    if (resp_leng>0) 
     response+= string(buffer).substr(0,resp_leng); 
} 


//display response 
cout << response << endl; 


myfile << response; 
myfile.close(); 

//disconnect 
closesocket(sock); 

//cleanup 
WSACleanup(); 
return 0; 
} 

void die_with_error(char *errorMessage) 
{ 
cerr << errorMessage << endl; 
    exit(1); 
} 

void die_with_wserror(char *errorMessage) 
{ 
    cerr << errorMessage << ": " << WSAGetLastError() << endl; 
    exit(1); 
} 

/* 

It compiles and runs fine the output is: 

HTTP/1.1 200 OK 
Server: Apache-Coyote/1.1 
ETag: W/"60-1144808550000" 
Last-Modified: Wed, 12 Apr 2006 02:22:30 GMT 
Content-Type: image/gif 
Content-Length: 60 
Date: Wed, 22 Feb 2012 04:29:04 GMT 
Connection: close 

GIF89a 
*/ 

什么是错的,为什么它不能下载的全貌?

+0

服务器上的图片有多少个字节? – 2012-02-22 04:42:16

+0

64.5kb这是一张10x10图片。 – 2012-02-22 04:43:32

+0

这很有趣,服务器说'内容长度:60'。 – 2012-02-22 04:44:35

回答

1

它看起来像这样循环的问题是:

while (resp_leng == BUFFERSIZE) 
{ 
    resp_leng= recv(sock, (char*)&buffer, BUFFERSIZE, 0); 
    if (resp_leng>0) 
     response+= string(buffer).substr(0,resp_leng); 
} 

调用recv()会给你一定数量的字节,从插座中,可能小于你要求什么。因此,与读取文件不同,询问BUFFERSIZE字节可能会返回1到BUFFERSIZE之间的任何字节。

相反,循环,直到你到插座的末尾:

while (true) 
{ 
    resp_leng= recv(sock, (char*)&buffer, BUFFERSIZE, 0); 
    if (resp_leng <= 0) { 
     break; 
    } 
    response+= string(buffer,resp_leng); 
} 

请注意,我也改变了调用string()构造上循环的最后一行(你的工作,但会做比必要的更多的努力)。

+0

我认为这有效。非常感谢你! :D – 2012-02-22 05:20:01

+0

读取直到'recv()'返回<= 0是解决这个问题的错误方法。读直到遇到空行,表示响应头的结束,然后继续读取,直到读取了“Content-Length”字节数。这就是'Content-Length'的作用,假设你甚至获得了'Content-Length'。阅读RFC 2616第4.4节,了解检测数据长度的所有可能方法。 – 2012-02-22 06:41:42

+0

嗯,那是真的。我将以我的答案为例,说明如何从循环中的套接字读取数据;解析HTTP标头超出了本示例的范围。 – 2012-02-22 07:30:35

0

此外,您应该清除每个周期的接收缓冲区。把它作为循环中的第一行。

ZeroMemory(buffer,sizeof(buffer));

您可以在缓冲区中留下二进制数据,以获得许多意想不到的结果。

+0

这不是必要的,而且是过度防御性编程的例子。当'recv()'返回一个值'n'(大于零)时,你可以确定缓冲区的第一个'n'字节已经接收到数据。这是我建议在我的答案中使用'string(buffer,resp_leng)'的一个原因。 – 2012-02-22 20:34:54