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).
#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_APPLETALK
Appletalk, etc.
Le type indique le style de communication désiré entre les deux participants. Les deux styles principaux sont
SOCK_DGRAM
: communication par messages (blocs contenant des
octets) appelés datagrammes SOCK_STREAM
: la communication se fait par un flot
(bidirectionnel) d'octets une fois que la connection est établie.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é.
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).
Dans l'exemple développé ici, un serveur affiche les datagrammes émis par les clients.
#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éponseeest 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 }
#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()
.
.
#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()
.
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);
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
accept(...)
qui permettra d'ajouter un
nouveau client à la liste.
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 }
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 }