2009-10-16 64 views
0

我需要一些幫助來編寫一個http客戶端。當我嘗試從網絡服務器接收數據時會遇到麻煩。 recv()調用阻止程序。任何更好的方向將是非常有益的,我會後我下面的代碼:關於recv上的http客戶端()

if (argc != 2) 
{ 
    cerr << "Usage: " << argv[0]; 
    cerr << " <URI>" << endl; 
    return 1; 
} 
else 
{ 
    uri_string = argv[1]; 
} 

// Create URI object and have it parse the uri_string 
URI *uri = URI::Parse(uri_string); 

if (uri == NULL) 
{ 
    cerr << "Error: Cannot parse URI." << endl; 
    return 2; 
} 

// Check the port number specified, if none use port 80 
unsigned port = 80; 
if (uri->Is_port_defined()) 
{ 
    port = uri->Get_port(); 
} 

// Create TCP socket and connect to server 
int tcp_sock = socket(AF_INET, SOCK_STREAM, 0); 
if (tcp_sock < 0) 
{ 
    cerr << "Unable to create TCP socket." << endl; 
    return 3; 
} 

sockaddr_in server; 
socklen_t slen = sizeof(server); 

server.sin_family = AF_INET; 
server.sin_port = htons(port); 
hostent *hostp = gethostbyname(uri->Get_host().c_str()); 
memcpy(&server.sin_addr, hostp->h_addr, hostp->h_length); 

if (connect(tcp_sock, (sockaddr*)&server, slen) < 0) 
{ 
    cerr << "Unable to connect to server via TCP." << endl; 
    close(tcp_sock); 
    return 4; 
} 

// Build HTTP request to send to server 
HTTP_Request *request = HTTP_Request::Create_GET_request(uri->Get_path()); 
request->Set_host(uri->Get_host()); 
string request_string = ""; 
request->Print(request_string); 

//cout << request_string << endl; 

// Send it to the server, wait for reply and use HTTP_Response to get reply 
send(tcp_sock, &request_string, sizeof(request_string), 0); 

char recv_buffer[1024]; 
int bytes_recv = 0; 
while (bytes_recv < 1024) 
{ 
    int recv_len = recv(tcp_sock, recv_buffer + bytes_recv, 
     1024 - bytes_recv, 0); 
    if (recv_len == -1) 
    { 
     cerr << "Error receiving response from server." << endl; 
     close(tcp_sock); 
     return 5; 
    } 
    bytes_recv += recv_len; 
} 


HTTP_Response *response = HTTP_Response::Parse(recv_buffer, bytes_recv); 
string response_string = ""; 
response->Print(response_string); 
cout << response_string << endl; 

return 0; 

}

回答

3

您正在使用阻塞的TCP/IP套接字,但您並未查看HTTP應答的「Content-Length」標頭以知道要讀取多少個字節。您當前的讀取邏輯在迴路中調用recv(),直到收到最大1024字節。如果服務器發送的字節少於1024個字節,則會被無限制地阻止,因爲您要求recv()多次請求太多的字節。

+0

感謝您的回覆,我想出了我需要做的事情。 – adhanlon 2009-10-21 15:46:59

+0

如果響應沒有任何「內容長度」標題,怎麼辦? – NeDark 2011-06-12 02:39:31

+0

@NeDark:閱讀RFC 2616(http://www.ietf.org/rfc/rfc2616.txt)第4.4節,它告訴你該怎麼做。 – 2011-06-23 09:54:30

0

這是一個問題?

如果客戶端comamnd線罰款。
如果是GUI,那麼線程檢索數據應該與UI線程不同。

但是一個解決方案是使用select()
這將告訴你是否有任何東西要從端口讀取。
因此,讓您在等待時做其他工作。

1

recv()假設阻塞,直到它得到響應。你確定你正確地編寫你的請求,並且服務器正在響應嗎?可以將文件描述符置於非阻塞模式,並使用select()poll()對其進行測試,但我的猜測是您只是在某處存在協議錯誤。你期望的行爲是什麼?

+0

我知道它應該阻塞,直到它得到的迴應,但它應該得到的迴應,當我打印出來我送它具有以下格式的Web服務器消息: GET/HTTP/1.1 Accept-Encoding:identity; q = 1.0,*; q = 0 Host:google.com TE:identity; q = 1.0,chunked; q = 0,*; q = 0 是否有任何理由爲什麼我不應該沒有從那得到迴應? – adhanlon 2009-10-17 01:35:58

+0

我想你需要問你的服務器,而不是我。 :)你看過一個數據包轉儲,以驗證命令到達服務器,產生一個響應,並返回到一塊? – 2009-10-17 03:16:19

0

HTTP請求應以一個空行結束,即


GET/HTTP/1.1 
Host: blah.com 
        <- this here is an empty line 

它看起來像你的代碼是不是這樣做,(它應該可能會說請求 - >打印(request_string + 「\ n」)

題外話:你知道在C中有現成的HTTP客戶端嗎?作爲libcurl)。

0

您必須接收許多字節內容長度字段。 您必須更改線路:if (recv_len == -1) 到:

if (recv_len <= 0) break; 
else if (recv_len == -1) 

因爲0是當服務器將發送所有數據後斷開連接。