2010-08-01 94 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工作。