Скачать HTTP через сокеты (C) - PullRequest
7 голосов
/ 01 августа 2010

Недавно я начал принимать это руководство , чтобы начать загружать файлы из Интернета.Я прочитал его и предложил следующий код, чтобы загрузить HTTP-текст веб-сайта.Единственная проблема, это не работает.Код останавливается при вызове recv ().Он не падает, просто продолжает работать.Это моя вина?Я использую неправильный подход?Я намерен использовать код не только для загрузки содержимого .html-файлов, но и для загрузки других файлов (zip, png, jpg, dmg ...).Я надеюсь, что есть кто-то, кто может мне помочь.Это мой код:

#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);
}

Ответы [ 3 ]

12 голосов
/ 01 августа 2010

Если вы хотите захватывать файлы с помощью HTTP, то libcURL , вероятно, является лучшим выбором для C. Однако, если вы используете это как способ изучения сетевого программирования, вам придетсяузнайте немного больше об HTTP, прежде чем вы сможете получить файл.

В текущей программе вы видите, что вам нужно отправить явный запрос на файл, прежде чем вы сможете его получить.Я бы начал с чтения RFC2616 .Не пытайтесь понять все это - это много, чтобы прочитать для этого примера.Прочтите первый раздел , чтобы понять, как работает HTTP, затем прочитайте разделы 4, 5 и 6 , чтобы понять основной формат сообщения.

ВотПример того, как выглядит HTTP-запрос для страницы вопросов stackoverflow:

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, чтобы показать, что для завершения блока заголовка запроса используется пустая строка, как описано в 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 1 и UNIX Network Programming, Volume 1 .Это, вероятно, лучший способ научиться писать сетевые приложения.Я бы, вероятно, начал с написания FTP-клиента , поскольку FTP - гораздо более простой протокол для начала.

Если вы пытаетесь узнать подробности, связанные с HTTP,затем:

  1. Купить HTTP: полное руководство и читать
  2. Читать RFC2616 до тех пор, пока вы его не поймете
    • Попробуйте примеры с использованием telnet server 80 и вводом запросов вручную
    • Загрузите клиент cURL и используйте параметры командной строки --verbose и --include, чтобы вы могли видеть, что происходит
  3. Прочитайте диссертацию Филдинга до тех пор, пока HTTP действительно не будет иметь смысла.

Только не планируйте писать свой собственный HTTP-клиент для корпоративного использования .Вы не хотите этого делать, поверьте мне, как тому, кто уже некоторое время допускает такую ​​ошибку ...

7 голосов
/ 01 августа 2010

Проблема в том, что вы должны реализовать протокол HTTP. Загрузка файла - это не просто соединение с сервером, вы должны отправлять HTTP-запросы (вместе с правильным HTTP-заголовком), прежде чем получите ответ. После этого вам все равно нужно будет проанализировать возвращаемые данные, чтобы убрать больше заголовков HTTP.

Если вы просто пытаетесь скачивать файлы с помощью C, я предлагаю cURL-библиотеку , которая работает для вас.

3 голосов
/ 01 августа 2010

Вы должны отправить HTTP-запрос, прежде чем ожидать ответа.Ваш код в настоящее время просто ждет ответа, который никогда не приходит.

Кроме того, не пишите комментарии во всех шапках.

...