Plusieurs processus enfants en lecture / écriture sur le même canal

J’apprends actuellement la programmation de sockets en utilisant le C dans un environnement Linux. En tant que projet, j’essaie d’écrire un client et un serveur de discussion de base.

L’intention est de faire en sorte que le serveur crée un processus pour chaque client qui se connecte.

Le problème que je rencontre est la lecture de données sur un enfant et leur écriture à tous les clients connectés.

J’ai essayé d’accomplir cela en effectuant une boucle sur un appel à sélectionner dans l’enfant qui attend que les données arrivent sur le socket ou lisent le bout du canal. Si elle arrive sur le socket, l’idée est qu’elle écrit dans l’écriture du tube, ce qui entraîne select à renvoyer l’extrémité lue du tube comme prête à être lue.

Comme ce canal est partagé entre tous les enfants, chaque enfant doit alors lire les données sur le canal. Cela ne fonctionne pas car il semble que le canal de données ne puisse pas être lu par chaque processus enfant en même temps et que les enfants qui “manquent” le bloc de données dans l’appel à lire.

Voici le code dans le processus enfant qui fait cela:

for( ; ; ) { rset = mset; if(select(maxfd+1, &rset, NULL, NULL, NULL) > 0) { if(FD_ISSET(clientfd, &rset)) { read(clientfd, buf, sizeof(buf)); write(pipes[1], buf, strlen(buf)); } if(FD_ISSET(pipes[0], &rset)) { read(pipes[0], buf, sizeof(buf)); write(clientfd, buf, sizeof(buf)); } } } 

Je suppose que la méthode que j’utilise actuellement ne fonctionnera tout simplement pas. Est-il possible que les messages reçus d’un client soient écrits sur tous les autres clients connectés via IPC?

Merci

Pour résoudre le problème d’un enfant lisant dans le canal plus de données qu’il ne le devrait (et obliger un autre enfant à restr bloqué en essayant de lire à partir d’un canal vide), vous devriez probablement envisager d’utiliser des files de messages POSIX ou un canal unique entre le processus parent et un processus enfant individuel plutôt qu’un canal global unique pour la communication entre les processus parent et enfant. Dans l’état actuel des choses, lorsque le serveur écrit dans le canal pour communiquer avec ses enfants, il n’est pas vraiment en mesure de contrôler exactement quel enfant lira dans le canal à un moment donné, car la planification des processus par le système d’exploitation n’est pas déterministe. . En d’autres termes, sans mécanisme de synchronisation ni barrière en lecture / écriture, si le serveur écrit dans le canal, rien dans votre code n’empêche un enfant de “sauter” une lecture et un second d’en faire une double lecture. , laissant un autre enfant qui aurait dû recevoir les données diffusées du serveur, et donc bloqué.

Un moyen simple de contourner ce problème pourrait encore être d’avoir un seul tuyau privé partagé entre le parent et un enfant individuel. Ainsi, dans le serveur, les processus enfants peuvent lire à partir du client, renvoyer ces données au processus parent et le parent peut ensuite, en utilisant la liste complète des descripteurs de canal accumulés pour tous les enfants, écrire à chaque processus enfant individuel. le message de diffusion qui est ensuite renvoyé à chaque client. Aucun enfant n’est jamais “privé” de données car il n’y a aucune possibilité de double lecture par un autre processus enfant. Il n’y a qu’un seul lecteur / auteur sur chaque pipe et la communication est déterministe.

Si vous ne souhaitez pas gérer plusieurs canaux pour chaque enfant dans le processus parent du serveur, vous pouvez utiliser une file de messages globale à l’aide de files de messages POSIX (disponibles dans mqueue.h ). Avec cette approche, si un enfant saisit un message qu’il n’est pas supposé avoir (c’est-à-dire que vous auriez besoin de passer une struct contenant un type de valeur d’ID), il remettrait le message dans la file d’attente et essaierait de lire une autre message … ce n’est pas aussi efficace en termes de rapidité que l’approche par canal direct, mais cela vous permettrait d’écrire un message qui n’a pas été conçu pour l’enfant actuel sans les complications d’entrelacement qui se produiraient avec un tube global ou Mécanisme FIFO.

Chaque octet de données écrit dans un canal sera lu exactement une fois. Il n’est pas dupliqué à chaque processus avec l’extrémité de lecture du tuyau ouverte.

Si vous souhaitez que les données soient dupliquées dans plusieurs processus de destination, vous devez les dupliquer explicitement. Par exemple, vous pouvez avoir un processus “maître” qui possède un canal vers et depuis chaque processus “esclave”. Lorsqu’un esclave veut diffuser un message aux autres esclaves, il l’envoie au processus maître, qui effectue une boucle et l’écrit une fois sur chaque canal allant aux autres esclaves.