TP 2 • poll
| Champ | Valeur |
|---|---|
| Auteur·e | Élise |
| Édition | 2024-04-03 |
| Durée | 2 sessions |
| Taille des équipes | 1~4 personnes login des auteurs·rices dans author.txt |
| Rendu | via git, dépôt $YEAR_lab_poll, droits en lecture à delivery_collector |
| Compilation | lignes de compilation dans justfile : exo1, exo2 |
| Binaires | N/A |
Notation
La réalisation de ce TP ne donne pas une note propre. Cependant, il contribuera à votre note finale de projet sur Clavardage si vous ne le réussissez pas.
Introduction
Quand on utilise des appels comme accept et read, on ne sait pas à l’avance s’ils vont bloquer. Sauf que si on a un programme qui doit pouvoir gérer plusieurs clients en même temps et de potentielles nouvelles connexion : bloquer sur un appel ça signifie ne rien pouvoir faire tant qu’il ne renvoie pas.
Pour exemple : mettons nous avons 2 clients connectés, on veut s’avoir si le premier à dit quelque chose donc on read sur sa socket. Il ne dit rien, donc l’appel bloque. Alors que nous sommes bloqués, le second client nous envoie un message. Problème : on est toujours bloqué par le premier write, donc on ne peut pas encore traiter le message. Tant que le client 1 n’envoie rien ou qu’il ne se déconnecte pas le programme ne pourra rien faire. Tout ce que le client 2 nous enverra, nous ne pourrons rien en faire. Le noyau garde une file d’attente des messages reçus sur le réseau qui n’ont pas encore été lus, mais au bout d’un moment, il y a un risque de pertes de données.
La question est donc : est-ce qu’il existe un moyen de savoir s’il y a quelque chose à lire sur un descripteur de fichier ?
La réponse : oui, il s’agit des appels systèmes poll et select. On va apprendre à se servir de poll car l’utilisation de select n’est pas recommandée pour les nouveaux projets, mais ça reste intéressant de connaitre son existence. Vous rencontrerez surement des “Selectors” à l’avenir dans d’autres langages de programmation ou dans des bibliothèques.
Poll
poll.h défini le prototype et la structure suivants :
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
struct pollfd {
int fd; /* file descriptor */
short events; /* requested events */
short revents; /* returned events */
};
Il y est aussi notamment défini les bitflags :
POLLINil y a quelque chose à lirePOLLOUTil est possible d’écrirePOLLHUPle pair à raccroché, s’est déconnecté (HUP = hang up)POLLERRil y a eu une erreur
On peut se servir de ses bitflags pour demander à poll de nous prévenir dans le cas où il y a quelque chose à lire ou s’il est possible d’écrire et lui nous préviendra si l’une de ses choses ou les deux est possible ou s’il y a eu une déconnexion ou une erreur.
La façon de s’y prendre est la suivante :
Déjà on travaille avec un tableau de struct pollfds :
struct pollfd *fds;
fds = malloc(sizeof(struct pollfd) * 2);
fds[0].fd = // [votre socket d'accept]
fds[0].events = POLLIN; // on veut être prévenus quand quelque chose arrive en entrée
fds[1].fd = // [votre premier client déjà accepté]
fds[1].events = POLLIN;
On peut passer le tableau et sa taille à poll :
int res_poll;
res_poll = poll(fds, 2, -1); // dernier arg à -1, regardez ce que ça veut dire dans le man
Après cet appel, poll va avoir enregistré les événements qu’on lui a demandé de surveiller, s’il se sont produits, ou les éventuelles erreurs/déconnexions reçues.
On peut les tester ainsi :
if (fds[0].revents & POLLIN) {
// il y a une connexion à accepter
}
if (fds[1].revents & POLLIN) {
// le client 1 nous parle
}
// etc
Il ne vous restera qu’à implémenter ce qu’il se passe ensuite dans le cas où :
- un client chercher à se connecter, le rajouter à votre
fds:- et donc penser à l’avance si vous allez gérer votre tableau à la façon d’un
vectorou d’unepool;
- et donc penser à l’avance si vous allez gérer votre tableau à la façon d’un
- un client vous parle ;
- un client s’est déconnecté :
- et penser à comment le retirer de vos
fds.
- et penser à comment le retirer de vos
Exercice : mini_netcat
Reprenez l’exercice connect, du premier TP et faites en sorte qu’à tout instant il soit possible de recevoir un message du serveur ou d’en écrire un sur l’entrée standard. La lecture sur STDIN ne doit pas bloquer. La lecture sur la socket ne doit pas bloquer.
Exercice : multi echo
Écrivez un programme qui fait office de serveur. Il prend en argument :
- le port sur lequel
bindet écouter ; - un nombre maximum de clients à gérer.
Le serveur doit pouvoir accepter de nouvelles connexions à tout instant. Lorsqu’un client envoie un message, il doit lui être renvoyé.
Lorsqu’un client se déconnecte, il doit être retiré de la liste de clients et ne compte plus dans la limite de client admissibles.