2015-05-26 69 views
2

SO。我試圖製作一個C應用程序,從服務器檢索.html文件,例如www.example.com。爲此,我使用套接字和connectsendrecv方法。我的實現看起來是這樣的:使用C中的套接字的HTTP請求

#include <sys/types.h> 
#include <sys/socket.h> 
#include <netdb.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <unistd.h> 
#include <string.h> 

int main(void) { 
    //Stream sockets and rcv() 

    struct addrinfo hints, *res; 
    int sockfd; 

    char buf[2056]; 
    int byte_count; 

    //get host info, make socket and connect it 
    memset(&hints, 0,sizeof hints); 
    hints.ai_family=AF_UNSPEC; 
    hints.ai_socktype = SOCK_STREAM; 
    getaddrinfo("www.example.com","80", &hints, &res); 
    sockfd = socket(res->ai_family,res->ai_socktype,res->ai_protocol); 
    printf("Connecting...\n"); 
    connect(sockfd,res->ai_addr,res->ai_addrlen); 
    printf("Connected!\n"); 
    char *header = "GET /index.html HTTP/1.1\nHost: www.example.com\n"; 
    send(sockfd,header,sizeof header,0); 
    printf("GET Sent...\n"); 
    //all right ! now that we're connected, we can receive some data! 
    byte_count = recv(sockfd,buf,sizeof buf,0); 
    printf("recv()'d %d bytes of data in buf\n",byte_count); 
    printf("%s",buf); 
    return 0; 
} 

但事實是,它被卡在recv幾秒鐘,然後緩衝buf充滿了這一點:

HTTP/1.0 408 Request Timeout 
Content-Type: text/html 
Content-Length: 431 
Connection: close 
Date: Tue, 26 May 2015 23:08:46 GMT 
Server: ECSF (fll/0781) 

<?xml version="1.0" encoding="iso-8859-1"?> 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> 
    <head> 
     <title>408 - Request Timeout</title> 
    </head> 
    <body> 
     <h1>408 - Request Timeout</h1> 
     <div>Server timeout waiting for the HTTP request from the client.</div> 
    </body> 
</html> 

顯然,服務器永遠不會得到我的GET字符串或它可能是錯誤的,那麼解決這個問題的正確方法是什麼?

我已經下載了libcurl,甚至在一個文件中獲得了http響應(這很好,所以我可以稍後處理它),但我非常希望手工完成。

我在這裏錯過了什麼?

回答

2

服務器超時的原因是因爲你沒有發送有效的請求。就像Halim指出的那樣,您的請求數據不完整,您正在使用LF而不是CRLF作爲換行符,並且缺少最後一個換行符來結束請求標頭。

但是,即使修復後,您的代碼仍然沒有正確發送請求。這是因爲您正在使用char*指向您的請求數據,然後將sizeof(char*)作爲數據長度傳遞給send()。因此,您僅發送4個字節("GET ")或8個字節("GET /ind"),具體取決於您是在編譯32位還是64位可執行文件。您需要使用strlen()而不是sizeof()

char *header = "GET /index.html HTTP/1.1\r\nHost: www.example.com\r\n\r\n"; 
send(sockfd,header,strlen(header),0); 

一旦你得到的那部分工作,你recv()邏輯不解析在所有服務器的響應,我以爲是你根本沒有得到那麼遠呢。但更重要的是,收到的數據不是空終止的,但recv()後面的printf()的呼叫假定它是。你需要解決這個問題爲好,無論是這樣的:

byte_count = recv(sockfd,buf,sizeof(buf)-1,0); // <-- -1 to leave room for a null terminator 
buf[byte_count] = 0; // <-- add the null terminator 
printf("recv()'d %d bytes of data in buf\n",byte_count); 
printf("%s",buf); 

或者,像這樣:

byte_count = recv(sockfd,buf,sizeof(buf),0); 
printf("recv()'d %d bytes of data in buf\n",byte_count); 
printf("%.*s",byte_count,buf); // <-- give printf() the actual data size 

,當然,不關你的代碼有任何錯誤在它處理的。你真的需要這樣做。

+0

我真的很困惑與sizeof和strlen的使用,我真的不知道我應該什麼時候使用它;上次發生在我身上的是套接字配置。這解決了我的問題,是的,我最近會處理錯誤。謝謝。 –

+1

'sizeof()'返回傳遞給它的任何字節大小。你傳給它一個指針,所以你可以返回指針本身的**大小**(32位爲4個字節,64位爲8個字節),* NOT *指向的數據大小!另一方面,當你將一個靜態數組傳遞給'sizeof()'時,你會得到整個數組的大小。 –

5

每個標題必須以\r\n而不是\n結尾。而且還有一個額外的\r\n在請求的末尾添加:

GET /index.html HTTP/1.1\r\n 
Host: www.example.com\r\n 
\r\n 
+0

然後,當你在它的時候,不要手動編寫HTTP代碼,使用第三方庫,如CURL。 –

+0

即使更改了我的消息應該看起來像使用HTTP 1.0,我仍然從服務器獲得相同的響應。 –

+0

@RemyLebeau我已經使用libcurl.h做了一個代碼,它工作的很好,但我想知道(而且我認爲我應該可以),如果我可以手動創建它。 :) –