Programmation Avancée en C


socket_TCP_server.c

00001 #include <arpa/inet.h>
00002 #include <netinet/in.h>
00003 #include <sys/socket.h>
00004 #include <sys/types.h>
00005 #include <sys/wait.h>
00006 #include <stdio.h>
00007 #include <stdlib.h>
00008 #include <string.h>
00009 #include <unistd.h>
00010 
00011 #define MAX_SIZE 256
00012 #define NUMPORT  6666     // Le port où les clients se connecteront.
00013 #define TAILLE_FILE 14    // Nombre maximal de connexions dans la file d'attente.
00014 
00015 void error(char * msg);      // Cf. ligne /*@\ref{network::socket_TCP_client::error} du listing~\ref{network::socket_TCP_client}@*/
00016 void traitement(int sockfd); // Cf. listing /*@\ref{network::socket_TCP_server::doit}@*/
00017 
00018 int main()
00019 {
00020         int sock_ecoute, desc_conn;     // desc_conn: pour la socket de travail.
00021         struct sockaddr_in adr_svr;     // Pour l'adresse locale du serveur.
00022         struct sockaddr_in adr_client;  // Pour l'adresse du client qui se connecte.
00023 
00024         /*** Création de la structure d'adresse locale du serveur. ***/
00025         memset(&adr_svr, 0, sizeof(adr_svr));
00026         adr_svr.sin_family      = AF_INET;
00027         adr_svr.sin_addr.s_addr = htonl(INADDR_ANY);
00028         adr_svr.sin_port        = htons(NUMPORT);
00029 
00030         /*** Création de la socket serveur. ***/
00031         if ((sock_ecoute = socket(PF_INET,SOCK_STREAM,0)) == -1)
00032                 error("[socket]");
00033 
00034         {/* On utilise la fonction setsockopt() pour autoriser la réutilisation de
00035             l'adresse locale au redémarrage du serveur sans délai. */
00036                 int on = 1;
00037                 if(setsockopt(sock_ecoute, SOL_SOCKET, SO_REUSEADDR, /*@\label{network::socket_TCP_server::setsockopt}@*/
00038                               &on, sizeof(on)))
00039                         error("[setsockopt(SO_REUSEADDR)]");
00040         }
00041 
00042         /*** Attachement de la socket. ***/
00043         if (bind(sock_ecoute, (struct sockaddr *) &adr_svr, sizeof(adr_svr)))
00044                 error("[bind]");
00045 
00046         /*** On en fait une socket d'écoute. ***/
00047         if (listen(sock_ecoute, TAILLE_FILE)) /*@\label{network::socket_TCP_server::listen}@*/
00048                 error("[listen]");
00049         printf("Serveur: à l'écoute à l'adresse %s sur le port %d\n",
00050                inet_ntoa(adr_svr.sin_addr), ntohs(adr_svr.sin_port));
00051 
00052         while(1) { // Boucle principale sur accept().
00053                 socklen_t addr_len = sizeof(adr_client);
00054                 desc_conn = accept(sock_ecoute, (struct sockaddr *) &adr_client, /*@\label{network::socket_TCP_server::accept}@*/
00055                                    &addr_len);
00056                 if (desc_conn == -1) {
00057                         perror("[accept]");
00058                         continue;
00059                 }
00060                 printf("Serveur: connexion acceptée avec %s:%d\n", 
00061                        inet_ntoa(adr_client.sin_addr), ntohs(adr_client.sin_port));
00062                 /*** Gestion des connexions acceptées avec des sous-processus. ***/
00063                 int pid = fork();
00064                 if (!pid) {         // Création d'un processus fils. /*@\label{network::socket_TCP_server::fork}@*/
00065                         close(sock_ecoute); /* Fermeture de la socket
00066                                                d'écoute par le fils. */
00067                         traitement(desc_conn); // Traitement de la requête.
00068                         return EXIT_SUCCESS;   // Sortie du processus fils.
00069                 }
00070                 close(desc_conn);          // Le père ferme la socket de travail.
00071                 if (pid == -1) { // Fin du programme si fork() échoue.
00072                         close(sock_ecoute);
00073                         return EXIT_FAILURE;
00074                 }
00075                 // Nettoyage des processus fils.
00076                 while(waitpid( -1, NULL, WNOHANG) > 0); /*@\label{network::socket_TCP_server::waitpid}@*/
00077         }
00078         close(sock_ecoute);   // Fermeture de la socket (jamais atteint...).
00079         return EXIT_SUCCESS;
00080 }
00081 
00082 void error(char * msg) {
00083         perror(msg);
00084         exit(EXIT_FAILURE);
00085 }
00086 
00087 /** Traite une connexion acceptée par envoi/reception de données */
00088 void traitement(int sockfd)
00089 {
00090         char requete[MAX_SIZE+1], reponse[MAX_SIZE] = "Nonnnnnnnnn !";
00091         size_t  answer_size = strlen(reponse)+1;
00092         size_t reste = answer_size;
00093 
00094         ssize_t n = read(sockfd, requete, MAX_SIZE-1); // Réception.
00095         if (n < 0) error("[read]");
00096         requete[n] = '\0'; // Précaution pour l'affichage.
00097         /* On peut n'avoir reçu qu'une requète partielle, pour certains programmes
00098            il peut falloir faire d'autres read pour la complèter. */
00099         printf(" - Requête reçue: <%s> (%ld octets reçus)\n", requete, (long) n);
00100 
00101         while (reste > 0) { // Émission de la réponse
00102                 n = write(sockfd, reponse + (answer_size - reste), reste);
00103                 if (n < 0) error("[write]");
00104                 printf(" - Réponse émise: <%s> (%ld octets envoyés)\n",
00105                        reponse, (long) n);
00106                 reste -= n;
00107         }
00108 }