Télécharger HTTP via les sockets (C)

cemment, j’ai commencé à utiliser ce guide pour commencer à télécharger des fichiers sur Internet. Je l’ai lu et suis venu avec le code suivant pour télécharger le corps HTTP d’un site Web. Le seul problème est que cela ne fonctionne pas. Le code s’arrête lors de l’appel de l’appel recv (). Il ne tombe pas en panne, il continue juste à courir. Est-ce ma faute? Est-ce que j’utilise la mauvaise approche? J’ai l’intention d’utiliser le code non seulement pour télécharger le contenu des fichiers .html, mais également pour télécharger d’autres fichiers (zip, png, jpg, dmg …). J’espère qu’il y a quelqu’un qui peut m’aider. Ceci est mon code:

#include  #include  /* SOCKET */ #include  /* struct addrinfo */ #include  /* exit() */ #include  /* memset() */ #include  /* errno */ #include  /* close() */ #include  /* IP Conversion */ #include  /* 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); } 

Si vous voulez récupérer des fichiers via HTTP, alors libcURL est probablement votre meilleur choix en C. Toutefois, si vous utilisez ceci pour apprendre la programmation réseau, vous devrez en apprendre un peu plus sur HTTP avant de pouvoir utiliser récupérer un fichier.

Ce que vous voyez dans votre programme actuel, c’est que vous devez envoyer une demande explicite pour le fichier avant de pouvoir le récupérer. Je commencerais par lire le document RFC2616 . N’essayez pas de tout comprendre – il y a beaucoup à lire pour cet exemple. Lisez la première section pour comprendre le fonctionnement de HTTP, puis lisez les sections 4, 5 et 6 pour comprendre le format de base du message.

Voici un exemple de requête HTTP pour la page Questions de 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 

Je crois que c’est une demande minimale. J’ai ajouté explicitement les CRLF pour montrer qu’une ligne vide est utilisée pour terminer le bloc d’en-tête de demande, comme décrit dans la RFC2616 . Si vous Accept-Encoding tête Accept-Encoding , le document de résultat sera probablement transféré sous forme de stream compressé au format gzip car HTTP le permet explicitement, sauf si vous indiquez au serveur que vous ne le souhaitez pas.

La réponse du serveur contient également des en-têtes HTTP pour les métadonnées décrivant la réponse. Voici un exemple de réponse de la requête précédente:

 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  

Cet exemple simple devrait vous donner une idée de ce que vous voulez implémenter si vous voulez récupérer des fichiers via HTTP. C'est le meilleur des cas, le plus simple exemple. Ce n'est pas quelque chose que je voudrais entreprendre à la légère, mais c'est probablement la meilleure façon d'apprendre et d'apprécier HTTP.

Si vous cherchez un moyen simple d’apprendre la programmation réseau, c’est une façon décente de commencer. Je vous conseillerais de vous procurer une copie de TCP / IP Illustrated, Volume 1 et UNIX Network Programming, Volume 1 . Celles-ci sont probablement le meilleur moyen d'apprendre réellement à écrire des applications basées sur le réseau. Je commencerais probablement par écrire un client FTP, car FTP est un protocole beaucoup plus simple.

Si vous essayez d'apprendre les détails associés à HTTP, alors:

  1. Achetez HTTP: le guide définitif et lisez-le
  2. Lisez la RFC2616 jusqu'à ce que vous la compreniez
    • Essayez des exemples en utilisant le telnet server 80 et en tapant les demandes à la main
    • Téléchargez le client cURL et utilisez les options de ligne de commande --verbose et --include afin de voir ce qui se passe.
  3. Lisez la thèse de Fielding jusqu'à ce que HTTP ait vraiment du sens.

N'envisagez tout simplement pas d'écrire votre propre client HTTP pour une utilisation en entreprise . Vous ne voulez pas faire cela, croyez-moi comme quelqu'un qui entretient une telle erreur depuis un moment maintenant ...

Le problème est que vous devez implémenter le protocole HTTP. Le téléchargement d’un fichier ne consiste pas uniquement à vous connecter au serveur, vous devez envoyer des requêtes HTTP (avec l’en-tête HTTP approprié) avant d’obtenir une réponse. Après cela, vous devez toujours parsingr les données renvoyées pour supprimer d’autres en-têtes HTTP.

Si vous essayez simplement de télécharger des fichiers en C, je vous suggère la bibliothèque cURL , qui fonctionne avec HTTP.

Vous devez envoyer une requête HTTP avant d’attendre une réponse. Votre code n’attend actuellement qu’une réponse qui n’arrive jamais.

En outre, n’écrivez pas de commentaires en majuscules.