Programmation Avancée en C


socket_TCP_client.c

00001 #include <arpa/inet.h>
00002 #include <netinet/in.h>
00003 #include <sys/socket.h>
00004 #define _POSIX_C_SOURCE 2 /* Nécessaire sur certains Linux. */
00005 #include <netdb.h>
00006 #include <stdio.h>
00007 #include <stdlib.h>
00008 #include <string.h>
00009 #include <unistd.h>
00010 
00011 #define MAX_SIZE 256
00012 
00013 void error(char * msg) { // Affichage d'un message d'erreur et sortie. /*@\label{network::socket_TCP_client::error}@*/
00014         perror(msg);
00015         exit(EXIT_FAILURE);
00016 }
00017 /*** Gestion des interactions avec le serveur ***/
00018 void doit(int sockfd, struct sockaddr_in * serv) {
00019         char requete[MAX_SIZE] = "Luc, je suis ton père!";
00020         char reponse[MAX_SIZE + 1];
00021         size_t taille_req = strlen(requete) + 1;
00022         size_t reste = taille_req;
00023 
00024         while (reste > 0) { // Envoi de la requête. /*@\label{network::socket_TCP_client::send}@*/
00025                 ssize_t n = send(sockfd, requete + (taille_req - reste), reste, 0);
00026                 if (n < 0) error("[send]");
00027                 printf("Requête envoyée à %s:%d : <%s> (%ld octets)\n",
00028                        inet_ntoa(serv->sin_addr), ntohs(serv->sin_port),
00029                        requete, (long) n);
00030                 reste -= n;
00031         }
00032         ssize_t n = recv(sockfd, reponse, MAX_SIZE-1, 0); // La réponse du serveur./*@\label{network::socket_TCP_client::recv}@*/
00033         if (n < 0) error("[recv]");
00034         reponse[n] = '\0'; // Précaution pour l'affichage.
00035         printf("Réponse obtenue: <%s> (%ld octets)\n", reponse, (long) n);
00036 }
00037 
00038 int main(int argc, char* argv[])
00039 {
00040         char * servname = "localhost";    // Serveur par défaut.
00041         char * port     = "6666";         // Port du service par défaut.
00042         struct addrinfo indices, *res;
00043         int err, sockfd = -1;
00044 
00045         switch (argc) {  // Traitement des arguments de la ligne de commande.
00046         case 3:  port     = argv[2];
00047         case 2:  servname = argv[1];
00048         case 1:  break;
00049         default: fprintf(stderr, "Usage: %s [servname [port]]\n",argv[0]);
00050                 return EXIT_FAILURE;
00051         }
00052         /*** Construction de l'adresse du serveur. ***/
00053         memset(&indices, 0, sizeof(indices));
00054         indices.ai_flags    = AI_CANONNAME; // On veut une résolution de nom.
00055         indices.ai_family   = AF_INET;      // Adresse IPv4 uniquement. /*@\label{network::socket_TCP_client::diffipv6_1}@*/
00056         indices.ai_socktype = SOCK_STREAM;  // Type "socket connectée".
00057         if ((err = getaddrinfo(servname, port, &indices, &res)))  {
00058                 if (err == EAI_SYSTEM) error("[getaddrinfo]");
00059                 fprintf(stderr, "[getaddrinfo] on %s : %s\n", servname,
00060                         gai_strerror(err));
00061                 return EXIT_FAILURE;
00062         }
00063 
00064         for(; res != NULL; res = res->ai_next) {
00065                 /*** Création d'une socket. ***/
00066                 sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
00067                 if (sockfd == -1) continue;
00068                 /*** Connexion au serveur. ***/
00069                 if (connect(sockfd, res->ai_addr, res->ai_addrlen)) {  /*@\label{network::socket_TCP_client::connect}@*/
00070                         close(sockfd);
00071                         sockfd = -1;
00072                         continue;
00073                 }
00074                 break; // Si on arrive ici, on a réussi à se connecter au serveur.
00075         }
00076         if (sockfd  < 0)   error("[socket ou connect]"); 
00077         doit(sockfd, (struct sockaddr_in *) res->ai_addr); // Gestion des requêtes. /*@\label{network::socket_TCP_client::diffipv6_2}@*/
00078         close(sockfd);     // Fermeture de la socket.
00079         freeaddrinfo(res); // Ne pas oublier de libérer l'espace alloué à res!
00080         return EXIT_SUCCESS;
00081 }
00082