2010-08-01 35 views
7

Recientemente comencé a tomar this guide para comenzar a descargar archivos de Internet. Lo leí y obtuve el siguiente código para descargar el cuerpo HTTP de un sitio web. El único problema es que no está funcionando. El código se detiene al llamar a la llamada recv(). No se cuelga, solo sigue funcionando. ¿Es esto mi culpa? ¿Estoy usando el enfoque incorrecto? Intento usar el código no solo para descargar el contenido de los archivos .html, sino también para descargar otros archivos (zip, png, jpg, dmg ...). Espero que haya alguien que pueda ayudarme. Este es mi código:Descargar HTTP a través de sockets (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

Si la intención es descargar archivos, no implementar HTTP, sería mejor que use una biblioteca como cURL: http://curl.haxx.se/ – You

Respuesta

12

Si desea tomar archivos usando HTTP, entonces libcURL es probablemente su mejor opción en C. Sin embargo, si está utilizando esto como una forma de aprender la programación de la red, entonces tendrá que aprender un poco más sobre HTTP antes de que puedas recuperar un archivo.

Lo que está viendo en su programa actual es que debe enviar una solicitud explícita del archivo antes de poder recuperarlo. Comenzaría leyendo a través del RFC2616. No trates de entenderlo todo; es mucho leer para este ejemplo. Lea el first section para comprender cómo funciona HTTP, luego lea las secciones 4, 5, and 6 para comprender el formato de mensaje básico.

Aquí es un ejemplo de lo que una petición HTTP para la página de Preguntas stackoverflow parece:

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 

Creo que es una petición mínima. Agregué los CRLF explícitamente para mostrar que se usa una línea en blanco para terminar el bloque de encabezado de solicitud as described in RFC2616. Si omite el encabezado Accept-Encoding, entonces el documento resultante probablemente se transfiera como una secuencia comprimida gzip, ya que HTTP permite esto explícitamente a menos que le indique al servidor que no lo desea.

La respuesta del servidor también contiene encabezados HTTP para los metadatos que describen la respuesta. Aquí está un ejemplo de una respuesta de la solicitud anterior:

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 

Este sencillo ejemplo que debe dar una idea de lo que está recibiendo en la implementación si quieres agarrar archivos a través de HTTP. Este es el mejor caso, el ejemplo más simple. Esto no es algo que emprendería a la ligera, pero probablemente sea la mejor forma de aprender y apreciar HTTP.

Si está buscando una manera simple de aprender programación en red, esta es una buena forma de comenzar. Yo recomendaría recoger una copia de TCP/IP Illustrated, Volume 1 y UNIX Network Programming, Volume 1. Estas son probablemente la mejor manera de aprender realmente cómo escribir aplicaciones basadas en red. Probablemente comenzaría escribiendo un FTP client ya que FTP es un protocolo mucho más simple para empezar.

Si usted está tratando de aprender los detalles asociados con HTTP, entonces:

  1. Comprar HTTP: the Definitive Guide y leerlo
  2. Leer RFC2616 hasta que entienda que
    • Trate ejemplos usando telnet server 80 y escribiendo solicitudes a mano
    • Descargue el cliente cURL y use las opciones de línea de comando --verbose y --include para que pueda ver lo que está sucediendo
  3. Lea Fielding's dissertation hasta que HTTP tenga sentido.

Simplemente no planifique escribir su propio cliente HTTP para uso empresarial. Usted no quiere hacer eso, créame como alguien que ha estado manteniendo un error así por un tiempo ...

+0

Realmente, realmente, realmente quiero agradecerles a todos por la respuesta rápida, especialmente D.Shawley. Supongo que descargar archivos no iba a ser tan fácil como pensaba, pero sin dudas lo conseguiré. Quiero que esto funcione porque quiero ser independiente de la biblioteca curl, y si no funciona ... cURL siempre estará allí. Gracias, ief2 – v1Axvw

+0

@ lef2. Eres bienvenido Sin embargo, ofreceré algunos consejos. El uso de implementaciones de protocolos complejos que otros ponen a disposición es una parte importante del desarrollo de software. Abrazaría bibliotecas como cURL, Apache Portable Runtime, Boost y otras bibliotecas populares. Escribir todo usted mismo es una receta para el desastre. Es una muy buena forma de aprender cómo funciona un protocolo, pero una muy mala manera de utilizar HTTP en la capa de aplicación. –

+0

Estaba de acuerdo con usted hasta que mencionó APR, que es la abominación más grande que he visto en C ... –

3

Tienes que enviar una solicitud HTTP antes de esperar una respuesta. El código actualmente solo espera una respuesta que nunca llega.

Además, no escriba comentarios con mayúsculas.

7

El problema es que debe implementar el protocolo HTTP. Descargar un archivo no es solo cuestión de conectarse al servidor, debe enviar solicitudes HTTP (junto con el encabezado HTTP adecuado) antes de obtener una respuesta. Después de esto, aún necesitaría analizar los datos devueltos para eliminar más encabezados HTTP.

Si solo intenta descargar archivos usando C, sugiero cURL library, que hace que el HTTP funcione para usted.

Cuestiones relacionadas