Page suivante Page précédente Table des matières

10. Communication interprocessus par sockets locaux

Les sockets (prises) sont un moyen générique de faire communiquer des processus entre eux. C'est le moyen standard utilisé pour la communication sur le réseau Internet, mais on peut l'employer également pour la communication locale entre processus tournant sur une même machine (commes les FIFOs et les pipes, et les files de messages IPC que nous verrons plus loin).

10.1 Création d'un socket

#include <sys/types.h>
#include <sys/socket.h>

int socket(int domain, int type, int protocol);

La fonction socket() crée une nouvelle prise et retourne un descripteur qui servira ensuite aux lectures et écritures. Le paramètre domain indique le « domaine de communication » utilisé, qui est PF_LOCAL ou (synonyme) PF_UNIX pour les communications locales.

Le domaine définit une famille de protocoles (protocol family) utilisables. Autres familles disponibles : PF_INET protocoles internet IPv4, PF_INET6 protocoles internet IPv6, PF_IPX protocoles Novel IPX, PF_X25 protocoles X25 (ITU-T X.25 / ISO-8208), PF_APPLETALKAppletalk, etc.

Le type indique le style de communication désiré entre les deux participants. Les deux styles principaux sont

Fiabilité: la fiabilité des communication par datagrammes est garantie pour le domaine local, mais ce n'est pas le cas pour les domaines « réseau » que nous verrons plus loin : les datagrammes peuvent être perdus, dupliqués, arriver dans le désordre etc. et c'est au programmeur d'application d'en tenir compte. Par contre la fiabilité des « streams » est assurée par les couches basses du système de communication, évidemment au prix d'un surcoût (numérotation des paquets, accusés de réception, temporisations, retranmissions, etc).

Enfin, le paramètre protocol indique le protocole sélectionné. La valeur 0 correspond au protocole par défaut pour le domaine et le type indiqué.

10.2 Adresses

La fonction socket() crée un socket anonyme. Pour qu'un autre processus puisse le désigner, il faut lui associer un nom par l'intermédiaire d'une adresse contenue dans une structure sockaddr_un :

#include <sys/un.h>

struct sockaddr_un {
        sa_family_t  sun_family;              /* AF_UNIX */
        char         sun_path[UNIX_PATH_MAX]; /* pathname */
};
Ces adresses sont des chemins d'accès dans l'arborescence des fichiers et répertoires.

Exemple :

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>

socklen_t longueur_adresse;

struct sockaddr_un  adresse;

adresse.sun_family      = AF_LOCAL;
strcpy(adresse.sun_path,"/tmp/xyz");
longueur_adresse = SUN_LEN (&dresse);  

L'association d'une adresse à un socket se fait par bind() (voir exemples plus loin).

10.3 Communication par datagrammes

Dans l'exemple développé ici, un serveur affiche les datagrammes émis par les clients.

La réception de datagrammes

#include <sys/types.h>
#include <sys/socket.h>

int  bind(int  sockfd, struct sockaddr *my_addr, 
                       socklen_t addrlen);

int  recvfrom(int  s,  void  *buf,  size_t len, int flags,
              struct sockaddr *from, socklen_t *fromlen);

La fonction bind() permet de nommer le socket de réception. La fonction recvfrom() attend l'arrivée d'un datagramme qui est stocké dans les len premiers octets du tampon buff. Si from n'est pas NULL, l'adresse du socket émetteur

qui peut servir à expédier une réponsee
est placée dans la structure pointée par from, dont la longueur maximale est contenue dans l'entier pointé par fromlen.

Si la lecture a réussi, la fonction retourne le nombre d'octets du message lu, et la longueur de l'adresse est mise à jour.

Le paramètre flags permet de préciser des options.


 
  1     /* serveur-dgram-local.c */

  2     /* 
  3        Usage:  serveur-dgram-local chemin

  4        Reçoit des datagrammes par un socket du domain local,
  5        et les affiche. Le paramètre indique le nom du socket.
  6        S'arrête quand la donnée est "stop".
  7     */

  8     #include <stdio.h>
  9     #include <unistd.h>
 10     #include <stdlib.h>
 11     #include <sys/types.h>
 12     #include <sys/socket.h>
 13     #include <sys/un.h>

 14     #define TAILLE_MAX_DONNEE 1024

 15     int main(int argc, char *argv[])
 16     {
 17             socklen_t longueur_adresse;
 18             struct sockaddr_un adresse;
 19             int fd;

 20             if (argc != 2) {
 21                     fprintf(stderr, "usage: %s chemin\n", argv[0]);
 22                     exit(EXIT_FAILURE);
 23             };

 24             adresse.sun_family = AF_LOCAL;
 25             strcpy(adresse.sun_path, argv[1]);
 26             longueur_adresse = SUN_LEN(&adresse);

 27             fd = socket(PF_LOCAL, SOCK_DGRAM, 0);
 28             if (fd < 0) {
 29                     perror("Création du socket serveur");
 30                     exit(EXIT_FAILURE);
 31             }

 32             if (bind(fd, (struct sockaddr *) &adresse, longueur_adresse) <
 33                 0) {
 34                     perror("Nommage du socket serveur");
 35                     exit(EXIT_FAILURE);
 36             }

 37             printf("> Serveur démarré sur socket local \"%s\"\n",
 38                    argv[1]);
 39             while (1) {
 40                     int lg;
 41                     /* un caractère supplémentaire permet d'ajouter le
 42                        terminateur de chaîne, qui n'est pas transmis */
 43                     char tampon[TAILLE_MAX_DONNEE + 1];
 44                     lg = recvfrom(fd, tampon, TAILLE_MAX_DONNEE, 0, NULL,
 45                                   NULL);
 46                     if (lg <= 0) {
 47                             perror("Réception datagramme");
 48                             exit(EXIT_FAILURE);
 49                     };
 50                     tampon[lg] = '\0';      /* ajout terminateur */
 51                     printf("Reçu : %s\n", tampon);
 52                     if (strcmp(tampon, "stop") == 0) {
 53                             printf("> arrêt demandé\n");
 54                             break;
 55                     }
 56             }
 57             close(fd);
 58             unlink(argv[1]);
 59             exit(EXIT_SUCCESS);
 60     }
 

Emission de datagrammes

#include <sys/types.h>
#include <sys/socket.h>

int  sendto(int s, const void *msg, size_t len, int flags,
            const struct sockaddr *to, socklen_t tolen);

sendto utilise le descripteur de socket s pour envoyer le message formé des len premiers octets de msg à l'adresse de longueur tolen pointée par to.

Le même descripteur peut être utilisé pour des envois à des adresses différentes.


 
  1     /* client-dgram-local.c */

  2     /* 
  3        Usage:  client-dgram-local chemin messages

  4        Envoie des datagrammes à un socket du domain local,
  5        et les affiche. Le premier paramètre indique le nom du socket,
  6        les autres des chaînes de caractères.

  7        Exemple : client-dgram-local un deux "trente et un" stop
  8     */

  9     #include <stdio.h>
 10     #include <unistd.h>
 11     #include <stdlib.h>
 12     #include <sys/types.h>
 13     #include <sys/socket.h>
 14     #include <sys/un.h>

 15     #define TAILLE_MAX_DONNEE 1024

 16     int main(int argc, char *argv[])
 17     {
 18             socklen_t longueur_adresse;
 19             struct sockaddr_un adresse;
 20             int fd;
 21             int k;

 22             if (argc <= 2) {
 23                     fprintf(stderr, "usage: %s chemin message\n",
 24                             argv[0]);
 25                     exit(EXIT_FAILURE);
 26             }

 27             adresse.sun_family = AF_LOCAL;
 28             strcpy(adresse.sun_path, argv[1]);
 29             longueur_adresse = SUN_LEN(&adresse);

 30             fd = socket(PF_LOCAL, SOCK_DGRAM, 0);
 31             if (fd < 0) {
 32                     perror("Création du socket client");
 33                     exit(EXIT_FAILURE);
 34             }

 35             for (k = 2; k < argc; k++) {
 36                     int lg;
 37                     lg = strlen(argv[k]);
 38                     if (lg > TAILLE_MAX_DONNEE)
 39                             lg = TAILLE_MAX_DONNEE;
 40                     /* le message est envoyé sans le terminateur '\0' */
 41                     if (sendto(fd, argv[k], lg, 0,
 42                                (struct sockaddr *) &adresse,
 43                                longueur_adresse) < 0) {
 44                             perror("Expédition du message");
 45                             exit(EXIT_FAILURE);
 46                     }
 47                     printf(".");
 48                     fflush(stdout);
 49                     sleep(1);
 50             }

 51             printf("Ok\n");
 52             close(fd);
 53             exit(EXIT_SUCCESS);
 54     }
 

Exercice : modifier les programmes précédents pour que le serveur envoie une réponse qui sera affichée par le client

Penser à attribuer un nom au socket du client pour que le serveur puisse lui répondre, par exemple avec l'aide de la fonction tempnam().
.

Emission et réception en mode connecté

#include <sys/types.h>
#include <sys/socket.h>

int  connect(int sockfd, 
             const struct sockaddr *serv_addr,
             socklen_t addrlen);

int send(int s, const void *msg, size_t len, int flags);
int recv(int s, void *buf, size_t len, int flags);

Un émetteur qui va envoyer une série de messages au même destinataire par le même socket peut faire préalablement un connect() pour indiquer une destination par défaut, et employer ensuite send() à la place de sendto().

Le récepteur qui ne s'intéresse pas à l'adresse de l'envoyeur peut utiliser recv().

Exercice : modifier les programmes précédents pour utiliser recv() et send().

10.4 Communication par flots

Dans ce type de communication, c'est une suite d'octets qui est transmises (et non pas une suite de messages comme dans la communication par datagrammes).

Les sockets locaux de ce type sont crées par

int fd = socket(PF_LOCAL,SOCK_STREAM,0);

Architecture client-serveur

La plupart des applications communicantes sont conçues selon une structure dissymétrique : l'architecture client-serveur, dans laquelle un processus serveur est contacté par plusieurs clients.

Le client crée un socket (socket()), qu'il met en relation (par connect()) avec celui du serveur. Les données sont échangées par read(), write() ... et le socket est fermé par close().

#include <sys/types.h>
#include <sys/socket.h>

int  connect(int sockfd, 
             const struct sockaddr *serv_addr,
             socklen_t addrlen);

Du côté serveur : un socket est crée et une adresse lui est associée (socket() + bind()). Un listen() prévient le système que ce socket recevra des demandes de connexion, et précise le nombre de connexions que l'on peut mettre en file d'attente.

Le serveur attend les demandes de connexion par la fonction accept() qui renvoit un descripteur, lequel permet la communication avec le client.

#include <sys/types.h>
#include <sys/socket.h>

int  bind(int             sockfd, 
          struct sockaddr *my_addr, 
          socklen_t       addrlen);

int  listen(int s, int backlog);       

int  accept(int s,  
            struct  sockaddr  *addr,  
            socklen_t         *addrlen);

Remarque : il est possible ne fermer qu'une ``moitié'' de socket : shutdown(1) met fin aux émissions (causant une ``fin de fichier'' chez le correspondant), shutdown(0) met fin aux réceptions.

       
#include <sys/socket.h>

int shutdown(int s, int how);

Le client :


 
  1     /*
  2       client-stream

  3       Envoi/réception de données par un socket local (mode connecté)

  4       Exemple de client qui 
  5       - ouvre un socket 
  6       - envoie sur ce socket du texte lu sur l'entree standard
  7       - attend et affiche une réponse
  8       */

  9     #include <unistd.h>
 10     #include <sys/types.h>
 11     #include <sys/socket.h>
 12     #include <sys/un.h>
 13     #include <signal.h>
 14     #include <stdio.h>
 15     #include <string.h>
 16     #include <stdlib.h>

 17     #define TAILLE_TAMPON 1000

 18     int main(int argc, char *argv[])
 19     {
 20             char *chemin;           /* chemin d'accès du socket serveur */
 21             socklen_t longueur_adresse;
 22             struct sockaddr_un adresse;
 23             int fd;

 24             /* 1. réception des paramètres de la ligne de commande */
 25             if (argc != 2) {
 26                     printf("Usage: %s chemin\n", argv[0]);
 27                     exit(EXIT_FAILURE);
 28             };
 29             chemin = argv[1];

 30             /* 2. Initialisation du socket */

 31             /* 2.1 création du socket   */
 32             fd = socket(PF_LOCAL, SOCK_STREAM, 0);
 33             if (fd < 0) {
 34                     perror("Création du socket client");
 35                     exit(EXIT_FAILURE);
 36             }

 37             /* 2.2 Remplissage adresse serveur */
 38             adresse.sun_family = AF_LOCAL;
 39             strcpy(adresse.sun_path, chemin);
 40             longueur_adresse = SUN_LEN(&adresse);

 41             /* 2.3 connexion au serveur */
 42             if (connect(fd,
 43                         (struct sockaddr *) &adresse, longueur_adresse)
 44                 < 0) {
 45                     perror("connect");
 46                     exit(EXIT_FAILURE);
 47             }
 48             printf("CLIENT> Connexion établie\n");

 49             /* 3. Lecture et envoi des données */
 50             for (;;) {
 51                     char tampon[TAILLE_TAMPON];
 52                     int nb_lus, nb_envoyes;
 53                     nb_lus = read(0, tampon, TAILLE_TAMPON);
 54                     if (nb_lus <= 0)
 55                             break;
 56                     nb_envoyes = write(fd, tampon, nb_lus);
 57                     if (nb_envoyes != nb_lus) {
 58                             perror("envoi données");
 59                             fprintf(stderr, "seulement %d envoyés sur %d",
 60                                     nb_envoyes, nb_lus);
 61                             exit(EXIT_FAILURE);
 62                     }
 63             }
 64             /* 4. Fin de l'envoi */
 65             shutdown(fd, 1);
 66             printf("CLIENT> Fin envoi, attente de la réponse.\n");

 67             /* 5. Réception et affichage de la réponse */
 68             for (;;) {
 69                     char tampon[TAILLE_TAMPON];
 70                     int nb_lus;
 71                     nb_lus = read(fd, tampon, TAILLE_TAMPON - 1);
 72                     if (nb_lus <= 0)
 73                             break;
 74                     tampon[nb_lus] = '\0';  /* ajout d'un terminateur de chaîne */
 75                     printf("%s", tampon);
 76             }
 77             /* et fin */
 78             close(fd);
 79             printf("CLIENT> Fin.\n");
 80             exit(EXIT_SUCCESS);
 81     }
 

Le serveur est programmé ici de façon atypique, puisqu'il traite qu'il traîte une seule communication à la fois. Si le client fait traîner les choses, les autres clients en attente resteront bloqués longtemps.


 
  1     /*
  2       serveur-stream

  3       Envoi/réception de données par un socket local (mode connecté)

  4       Exemple de serveur qui 
  5       - attend une connexion
  6       - lit du texte
  7       - envoie une réponse

  8       Remarque: ce serveur ne traite qu'une connexion à la fois.

  9      */

 10     #include <unistd.h>
 11     #include <sys/types.h>
 12     #include <sys/socket.h>
 13     #include <sys/un.h>
 14     #include <signal.h>
 15     #include <stdio.h>
 16     #include <stdlib.h>
 17     #include <ctype.h>

 18     #define TAILLE_TAMPON 1000
 19     #define CONNEXIONS_EN_ATTENTE 4

 20     int main(int argc, char *argv[])
 21     {
 22             int fd_serveur;
 23             struct sockaddr_un adresse;
 24             size_t taille_adresse;
 25             char *chemin;
 26             char tampon[TAILLE_TAMPON];
 27             int nb_car;
 28             int numero_connexion = 0;

 29             /* 1. réception des paramètres de la ligne de commande */

 30             if (argc != 2) {
 31                     printf("usage: %s chemin\n", argv[0]);
 32                     exit(1);
 33             };
 34             chemin = argv[1];

 35             /* 3. Initialisation du socket de réception */
 36             /* 3.1 création du socket   */
 37             fd_serveur = socket(PF_LOCAL, SOCK_STREAM, 0);
 38             if (fd_serveur < 0) {
 39                     perror("Création du socket serveur");
 40                     exit(EXIT_FAILURE);
 41             }

 42             /* 3.2 Remplissage adresse serveur */
 43             adresse.sun_family = AF_LOCAL;
 44             strcpy(adresse.sun_path, chemin);
 45             taille_adresse = SUN_LEN(&adresse);

 46             /* 3.3 Association de l'adresse au socket */
 47             taille_adresse = sizeof adresse;
 48             if (bind(fd_serveur,
 49                      (struct sockaddr *) &adresse, taille_adresse) < 0) {
 50                     perror("bind");
 51                     exit(EXIT_FAILURE);
 52             }
 53             /* 3.4 Ce socket attend des connexions mises en file d'attente */
 54             listen(fd_serveur, CONNEXIONS_EN_ATTENTE);

 55             printf("SERVEUR> Le serveur écoute le socket %s\n", chemin);

 56             /* 4. boucle du serveur */

 57             for (;;) {
 58                     int fd_client;
 59                     int compteur = 0;

 60                     /* 4.1 attente d'une connexion */
 61                     printf("SERVEUR> Attente d'une connexion.\n");
 62                     fd_client = accept(fd_serveur, NULL, NULL);
 63                     if (fd_client < 0) {
 64                             perror("accept");
 65                             exit(EXIT_FAILURE);
 66                     }
 67                     numero_connexion++;
 68                     printf("SERVEUR> Connexion #%d établie.\n",
 69                            numero_connexion);

 70                     /* 4.2 lecture et affichage des données envoyées par le client */
 71                     for (;;) {
 72                             int nb_lus;
 73                             nb_lus =
 74                                 read(fd_client, tampon,
 75                                      TAILLE_TAMPON - 1);
 76                             if (nb_lus <= 0)
 77                                     break;
 78                             compteur += nb_lus;
 79                             tampon[nb_lus] = '\0';
 80                             printf("%s", tampon);
 81                     };

 82                     /* 4.4 plus de données, on envoit une réponse */
 83                     printf("SERVEUR> envoi de la réponse.\n");
 84                     nb_car =
 85                         sprintf(tampon, "*** Fin de la connexion #%d\n",
 86                                 numero_connexion);
 87                     write(fd_client, tampon, nb_car);
 88                     nb_car =
 89                         sprintf(tampon,
 90                                 "*** Vous m'avez envoyé %d caractères\n",
 91                                 compteur);
 92                     write(fd_client, tampon, nb_car);
 93                     nb_car = sprintf(tampon, "*** Merci et à bientot.\n");
 94                     write(fd_client, tampon, nb_car);

 95                     /* 4.4 fermeture de la connexion */
 96                     close(fd_client);
 97                     printf("SERVEUR> fin de connexion.\n");
 98             }
 99     }
 

Dans une programmation plus classique, le serveur lance un processus (par fork(), voir plus loin) dès qu'une connexion est établie, et délègue le traitement de la connexion à ce processus.

Une autre technique est envisageable pour traiter plusieurs connexions par un processus unique : le serveur maintient une liste de descripteurs ouverts, et fait une boucle autour d'un select(), en attente de données venant

Cette technique conduit à des performances nettement supérieures aux serveurs multiprocessus ou multithreads (pas de temps perdu à lancer des processus), au prix d'une programmation qui oblige le programmeur à gérer lui-même le ``contexte de déroulement'' de chaque processus.


 
  1     /*
  2       serveur-stream-monotache

  3       Envoi/réception de données par un socket local (mode connecté)

  4       Exemple de serveur monotâche qui gère plusieurs connexions

  5       - attend une connexion
  6       - lit du texte
  7       - envoie une réponse
  8      */

  9     #include <unistd.h>
 10     #include <sys/types.h>
 11     #include <sys/socket.h>
 12     #include <sys/un.h>
 13     #include <signal.h>
 14     #include <stdio.h>
 15     #include <stdlib.h>
 16     #include <ctype.h>
 17     #include <assert.h>

 18     #define TAILLE_TAMPON 1000
 19     #define CONNEXIONS_EN_ATTENTE 4
 20     #define NBMAXCLIENTS 10

 21     /* les données propres à chaque client */

 22     #define INACTIF -1
 23     struct {
 24             int fd;
 25             int numero_connexion;
 26             int compteur;
 27     } client[NBMAXCLIENTS];

 28     int main(int argc, char *argv[])
 29     {
 30             int fd_serveur;
 31             struct sockaddr_un adresse;
 32             size_t taille_adresse;
 33             char *chemin;
 34             int nb_connexions = 0;
 35             int i;
 36             int nbfd;

 37             /* 1. réception des paramètres de la ligne de commande */

 38             if (argc != 2) {
 39                     printf("usage: %s chemin\n", argv[0]);
 40                     exit(1);
 41             };
 42             chemin = argv[1];

 43             /* 3. Initialisation du socket de réception */
 44             /* 3.1 création du socket   */
 45             fd_serveur = socket(PF_LOCAL, SOCK_STREAM, 0);
 46             if (fd_serveur < 0) {
 47                     perror("Création du socket serveur");
 48                     exit(EXIT_FAILURE);
 49             }

 50             /* 3.2 Remplissage adresse serveur */
 51             adresse.sun_family = AF_LOCAL;
 52             strcpy(adresse.sun_path, chemin);
 53             taille_adresse = SUN_LEN(&adresse);

 54             /* 3.3 Association de l'adresse au socket */
 55             taille_adresse = sizeof adresse;
 56             if (bind(fd_serveur,
 57                      (struct sockaddr *) &adresse, taille_adresse) < 0) {
 58                     perror("bind");
 59                     exit(EXIT_FAILURE);
 60             }
 61             /* 3.4 Ce socket attend des connexions mises en file d'attente */
 62             listen(fd_serveur, CONNEXIONS_EN_ATTENTE);

 63             printf("SERVEUR> Le serveur écoute le socket %s\n", chemin);

 64             /* 3.5 initialisation du tableau des clients */
 65             for (i = 0; i < NBMAXCLIENTS; i++) {
 66                     client[i].fd = INACTIF;
 67             }

 68             /* 4. boucle du serveur */

 69             for (;;) {
 70                     fd_set lectures;

 71                     /* 4.1 remplissage des masques du select */
 72                     FD_ZERO(&lectures);
 73                     FD_SET(fd_serveur, &lectures);
 74                     for (i = 0; i < NBMAXCLIENTS; i++) {
 75                             if (client[i].fd != INACTIF)
 76                                     FD_SET(client[i].fd, &lectures);
 77                     }

 78                     /* 4.2 attente d'un événement (ou plusieurs) */
 79                     nbfd =
 80                         select(FD_SETSIZE, &lectures, NULL, NULL, NULL);
 81                     assert(nbfd >= 0);

 82                     /* 4.3 en cas de nouvelle connexion : */
 83                     if (FD_ISSET(fd_serveur, &lectures)) {
 84                             /* si il y a de la place dans la table des clients, 
 85                                on y ajoute la nouvelle connexion */
 86                             nbfd--;
 87                             for (i = 0; i < NBMAXCLIENTS; i++) {
 88                                     if (client[i].fd == INACTIF) {
 89                                             int fd_client;
 90                                             fd_client =
 91                                                 accept(fd_serveur, NULL,
 92                                                        NULL);
 93                                             if (fd_client < 0) {
 94                                                     perror("accept");
 95                                                     exit(EXIT_FAILURE);
 96                                             }
 97                                             client[i].fd = fd_client;
 98                                             client[i].numero_connexion =
 99                                                 ++nb_connexions;
100                                             client[i].compteur = 0;
101                                             printf
102                                                 ("SERVEUR> arrivée de connexion #%d (fd %d)\n"
103                                                  client[i].
104                                                  numero_connexion,
105                                                  fd_client);
106                                             break;
107                                     }
108                                     if (i >= NBMAXCLIENTS) {
109                                             printf
110                                                 ("SERVEUR> trop de connexions !\n");
111                                     }
112                             }
113                     };

114                     /* 4.4 traitement des clients actifs qui ont reçu des données */
115                     for (i = 0; (i < NBMAXCLIENTS) && (nbfd > 0); i++) {
116                             if ((client[i].fd != INACTIF) &&
117                                 FD_ISSET(client[i].fd, &lectures)) {
118                                     int nb_lus;
119                                     char tampon[TAILLE_TAMPON];
120                                     nbfd--;
121                                     nb_lus =
122                                         read(client[i].fd, tampon,
123                                              TAILLE_TAMPON - 1);
124                                     printf
125                                         ("SERVEUR> données reçues de #%d (%d octets)\n",
126                                          client[i].numero_connexion,
127                                          nb_lus);
128                                     if (nb_lus > 0)
129                                             client[i].compteur += nb_lus;
130                                     else {
131                                             int nb_car;
132                                             printf
133                                                 ("SERVEUR> envoi de la réponse au client #%d.\
134                                                  client[i].
135                                                  numero_connexion);
136                                             nb_car =
137                                                 sprintf(tampon,
138                                                         "*** Fin de la connexion #%d\n",
139                                                         client[i].
140                                                         numero_connexion);
141                                             write(client[i].fd, tampon,
142                                                   nb_car);
143                                             nb_car =
144                                                 sprintf(tampon,
145                                                         "*** Vous m'avez envoyé %d caractères\n",
146                                                         client[i].
147                                                         compteur);
148                                             write(client[i].fd, tampon,
149                                                   nb_car);
150                                             nb_car =
151                                                 sprintf(tampon,
152                                                         "*** Merci et à bientôt.\n");
153                                             write(client[i].fd, tampon,
154                                                   nb_car);
155                                             close(client[i].fd);
156                                             /* enlèvement de la liste des clients */
157                                             client[i].fd = INACTIF;
158                                     }
159                             }
160                     }
161                     assert(nbfd == 0);      /* tous les descripteurs ont été traités */
162             }
163     }
 

10.5 socketpair()

La fonction socketpair() construit une paire de sockets locaux, bi-directionnels, reliés l'un à l'autre.

#include <sys/types.h>
#include <sys/socket.h>

int socketpair(int d, int type, int protocol, int sv[2]);

Dans l'état actuel des implémentations, le paramètre d (domaine) doit être égal à AF_LOCAL, et type à SOCK_DGRAM ou SOCK_STREAM, avec le protocole par défaut (valeur 0).

Cette fonction remplit le tableau sv[] avec les descripteurs de deux sockets du type indiqué. Ces deux sockets sont reliés entre eux et bidirectionnels : ce qu'on écrit sur le descripteur sv[0] peut être lu sur sv[1], et réciproquement.

On utilise socketpair() comme pipe(), pour la communication entre descendants d'un même processus

au sens large, ce qui inclue la communication d'un processus avec un de ses fils
. socketpair() possède deux avantages sur pipe() : la possibilité de transmettre des datagrammes, et la bidirectionnalité.

Exemple :


 
  1     /*
  2       paire-send.c

  3       échange de données à travers des sockets locaux créés par
  4       socketpair()
  5     */

  6     #include <unistd.h>
  7     #include <stdio.h>
  8     #include <stdlib.h>
  9     #include <sys/socket.h>
 10     #include <sys/types.h>

 11     struct paquet {
 12             int type;
 13             int valeur;
 14     };

 15     /* les différents types de paquets */

 16     #define DONNEE   0
 17     #define RESULTAT 1
 18     #define FIN      2

 19     void additionneur(int fd)
 20     {
 21             /*
 22                calcule la somme des entiers qui arrivent sur le descripteur,
 23                renvoie le résultat
 24              */
 25             int somme = 0;
 26             struct paquet p;
 27             int n;

 28             for (;;) {
 29                     n = recv(fd, &p, sizeof p, 0);
 30                     if (n < 0) {
 31                             perror("additionneur : réception");
 32                             exit(EXIT_FAILURE);
 33                     };
 34                     printf
 35                         ("additionneur: réception d'un paquet contenant ");
 36                     if (p.type == FIN) {
 37                             printf("la marque de fin\n");
 38                             break;
 39                     }
 40                     printf("la donnée %d\n", p.valeur);
 41                     somme += p.valeur;
 42             };
 43             /* envoi reponse */
 44             p.type = RESULTAT;
 45             p.valeur = somme;
 46             printf("additionneur: envoi du total %d\n", somme);
 47             n = send(fd, &p, sizeof p, 0);
 48             if (n < 0) {
 49                     perror("additionneur : envoi");
 50                     exit(EXIT_FAILURE);
 51             };
 52             exit(EXIT_SUCCESS);
 53     };

 54     void generateur(int fd)
 55     {
 56             /*
 57                envoie une suite d'entiers, récupère et affiche le résultat.
 58              */
 59             struct paquet p;
 60             int i, n, resultat;

 61             for (i = 1; i <= 5; i++) {
 62                     p.type = DONNEE;
 63                     p.valeur = i;
 64                     printf("génerateur: envoi de la donnée %d\n", i);
 65                     n = send(fd, &p, sizeof p, 0);
 66                     if (n < 0) {
 67                             perror("generateur: envoi donnée");
 68                             exit(EXIT_FAILURE);
 69                     };
 70                     sleep(1);
 71             };
 72             p.type = FIN;
 73             printf("génerateur: envoi de la marque de fin\n");
 74             n = send(fd, &p, sizeof p, 0);
 75             if (n < 0) {
 76                     perror("generateur: envoi fin");
 77                     exit(EXIT_FAILURE);
 78             };

 79             printf("generateur: lecture du résultat\n");
 80             n = recv(fd, &p, sizeof p, 0);
 81             if (n < 0) {
 82                     perror("generateur: réception réponse");
 83                     exit(EXIT_FAILURE);
 84             };
 85             resultat = p.valeur;
 86             printf("generateur: resultat reçu = %d\n", resultat);
 87     }

 88     int main()
 89     {
 90             int paire[2];

 91             socketpair(AF_LOCAL, SOCK_DGRAM, 0, paire);

 92             if (fork() == 0) {
 93                     close(paire[0]);
 94                     additionneur(paire[1]);
 95                     exit(EXIT_SUCCESS);
 96             };
 97             close(paire[1]);
 98             generateur(paire[0]);
 99             exit(EXIT_SUCCESS);
100     }
 


Page suivante Page précédente Table des matières