2010-08-01 92 views
7

最近我開始採取this guide讓自己開始從互聯網上下載文件。我讀了它,並提出了下面的代碼來下載網站的HTTP正文。唯一的問題是,它不工作。調用recv()調用時代碼停止。它不會崩潰,它只是繼續運行。這是我的錯嗎?我使用錯誤的接近?我打算使用代碼不僅下載.html文件的內容,而且還下載其他文件(zip,png,jpg,dmg ...)。我希望有人能幫助我。這是我的代碼:下載HTTP通過套接字(C)

#include <stdio.h> 
#include <sys/socket.h> /* SOCKET */ 
#include <netdb.h> /* struct addrinfo */ 
#include <stdlib.h> /* exit() */ 
#include <string.h> /* memset() */ 
#include <errno.h> /* errno */ 
#include <unistd.h> /* close() */ 
#include <arpa/inet.h> /* IP Conversion */ 

#include <stdarg.h> /* va_list */ 

#define SERVERNAME "developerief2.site11.com" 
#define PROTOCOL "80" 
#define MAXDATASIZE 1024*1024 

void errorOut(int status, const char *format, ...); 
void *get_in_addr(struct sockaddr *sa); 

int main (int argc, const char * argv[]) { 
    int status; 

    // GET ADDRESS INFO 
    struct addrinfo *infos; 
    struct addrinfo hints; 

    // fill hints 
    memset(&hints, 0, sizeof(hints)); 
    hints.ai_socktype = SOCK_STREAM; 
    hints.ai_flags = AI_PASSIVE; 
    hints.ai_family = AF_UNSPEC; 

    // get address info 
    status = getaddrinfo(SERVERNAME, 
         PROTOCOL, 
         &hints, 
         &infos); 
    if(status != 0) 
     errorOut(-1, "Couldn't get addres information: %s\n", gai_strerror(status)); 

    // MAKE SOCKET 
    int sockfd; 

    // loop, use first valid 
    struct addrinfo *p; 
    for(p = infos; p != NULL; p = p->ai_next) { 
     // CREATE SOCKET 
     sockfd = socket(p->ai_family, 
         p->ai_socktype, 
         p->ai_protocol); 
     if(sockfd == -1) 
      continue; 

     // TRY TO CONNECT 
     status = connect(sockfd, 
         p->ai_addr, 
         p->ai_addrlen); 
     if(status == -1) { 
      close(sockfd); 
      continue; 
     } 

     break; 
    } 

    if(p == NULL) { 
     fprintf(stderr, "Failed to connect\n"); 
     return 1; 
    } 

    // LET USER KNOW 
    char printableIP[INET6_ADDRSTRLEN]; 
    inet_ntop(p->ai_family, 
       get_in_addr((struct sockaddr *)p->ai_addr), 
       printableIP, 
       sizeof(printableIP)); 
    printf("Connection to %s\n", printableIP); 

    // GET RID OF INFOS 
    freeaddrinfo(infos); 

    // RECEIVE DATA 
    ssize_t receivedBytes; 
    char buf[MAXDATASIZE]; 
    printf("Start receiving\n"); 
    receivedBytes = recv(sockfd, 
         buf, 
         MAXDATASIZE-1, 
         0); 
    printf("Received %d bytes\n", (int)receivedBytes); 
    if(receivedBytes == -1) 
     errorOut(1, "Error while receiving\n"); 

    // null terminate 
    buf[receivedBytes] = '\0'; 

    // PRINT 
    printf("Received Data:\n\n%s\n", buf); 

    // CLOSE 
    close(sockfd); 

    return 0; 
} 

void *get_in_addr(struct sockaddr *sa) { 
    // IP4 
    if(sa->sa_family == AF_INET) 
     return &(((struct sockaddr_in *) sa)->sin_addr); 

    return &(((struct sockaddr_in6 *) sa)->sin6_addr); 
} 

void errorOut(int status, const char *format, ...) { 
    va_list args; 
    va_start(args, format); 
    vfprintf(stderr, format, args); 
    va_end(args); 
    exit(status); 
} 
+2

如果意圖是下載文件,不實施HTTP,你會更好地使用庫如cURL:http://curl.haxx.se/ – You 2010-08-01 13:23:27

回答

12

如果你想使用HTTP抓取文件,那麼libcURL可能是你在C中最好的選擇。但是,如果你使用它作爲學習網絡編程的一種方式,那麼你將不得不學習更多關於HTTP,然後才能檢索文件。

你在當前程序中看到的是,你需要發送一個明確的文件請求,然後才能找回它。我會從RFC2616開始閱讀。不要試圖理解這一切 - 這個例子閱讀很多。閱讀first section以瞭解HTTP的工作原理,然後閱讀4, 5, and 6以瞭解基本的消息格式。

這裏是一個什麼樣的計算器問題頁面的HTTP請求看起來像一個例子:

GET http://stackoverflow.com/questions HTTP/1.1\r\n 
Host: stackoverflow.com:80\r\n 
Connection: close\r\n 
Accept-Encoding: identity, *;q=0\r\n 
\r\n 

我相信這是一個很小的請求。我明確添加了CRLF,以顯示空白行用於終止請求標題塊as described in RFC2616。如果忽略Accept-Encoding標題,則結果文檔可能會被轉換爲gzip壓縮流,因爲HTTP會明確地允許這一點,除非您告訴服務器您不需要它。

服務器響應還包含描述響應的元數據的HTTP標頭。這是從以前的請求的響應的一個例子:

HTTP/1.1 200 OK\r\n 
Server: nginx\r\n 
Date: Sun, 01 Aug 2010 13:54:56 GMT\r\n 
Content-Type: text/html; charset=utf-8\r\n 
Connection: close\r\n 
Cache-Control: private\r\n 
Content-Length: 49731\r\n 
\r\n 
\r\n 
\r\n 
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" ... 49,667 bytes follow 

這個簡單的例子應該給你一個想法是什麼你做了,如果你想使用HTTP抓取的文件執行。這是最好的例子,最簡單的例子。這不是我輕易承擔的,但它可能是學習和欣賞HTTP的最佳方式。

如果您正在尋找一種簡單的方法來學習網絡編程,這是一個體面的開始方式。我會建議拿起TCP/IP Illustrated, Volume 1UNIX Network Programming, Volume 1的副本。這些可能是真正學習如何編寫基於網絡的應用程序的最佳方法。我可能會從編寫FTP client開始,因爲FTP是一個非常簡單的協議。

如果你正在努力學習與HTTP相關的詳細信息,然後:

  1. 購買HTTP: the Definitive Guide和閱讀
  2. 閱讀RFC2616,直到你明白使用telnet server 80並鍵入它
    • 嘗試實例手動請求
    • 下載cURL客戶端並使用--verbose--include命令行選項這樣你就可以看到發生了什麼
  3. 閱讀Fielding's dissertation直到HTTP真的有意義。

只是不打算編寫自己的HTTP客戶端企業使用。你不想這樣做,相信我一直在維持這樣一個錯誤一個現在...

+0

我真的非常非常感謝大家的快速回復,特別是D.Shawley。我猜下載文件不會像我想象的那麼容易,但我一定會得到這個工作。我希望這樣做,因爲我想獨立於捲曲庫,如果它不起作用...... cURL將始終存在。 謝謝, ief2 – v1Axvw 2010-08-01 14:56:59

+0

@ lef2。你很受歡迎。我會提供一些建議。使用其他人提供的複雜協議的實現是開發軟件的重要部分。我會接受像cURL,Apache Portable Runtime,Boost和其他流行庫這樣的庫。自己寫一切都是災難的祕訣。這是學習協議如何工作的好方法,但是在應用層使用HTTP的方式非常糟糕。 – 2010-08-01 15:20:06

+0

我同意你的意見,直到你提到APR,這是我在C中見過的最大的憎惡...... – 2010-08-01 17:30:39

3

您必須在期待響應之前發送HTTP請求。您目前的代碼只是等待一個永遠不會到來的響應。

另外,不要寫全部大寫的註釋。

7

問題是,你必須實現HTTP協議。下載文件不僅僅是連接到服務器,您必須在獲得響應之前發送HTTP請求(以及正確的HTTP標頭)。在此之後,您仍然需要解析返回的數據以去除更多的HTTP標頭。

如果你只是想用C下載文件,我建議cURL library,它爲你做HTTP工作。