La boucle d’événements
Réagissez aux sockets lisibles.
La boucle d’événements est une leçon C Academy gratuite sur CoddyKit. Ceci est la leçon 3 sur 4. Tu peux lire la leçon complète ci-dessous gratuitement — puis la pratiquer en direct dans le navigateur avec un éditeur de code intégré et un tuteur IA 24/7. Elle fait partie du parcours d'apprentissage C Academy, et ta progression se synchronise sur le web et l'application CoddyKit. Le cours C Academy comprend 4 leçons au total.
Le cœur du serveur
La boucle d’événements est le moteur d’un serveur epoll. Elle attend sans cesse que des descripteurs soient prêts, puis distribue le travail aux gestionnaires.
Sa structure est toujours la même : appeler epoll_wait(), parcourir les événements renvoyés, agir sur chacun d’eux, puis recommencer. Tout le reste s’appuie sur cette structure.
for (;;) {
int n = epoll_wait(epfd, events, MAX_EVENTS, -1);
for (int i = 0; i < n; i++) handle(&events[i]);
}Appeler epoll_wait
epoll_wait() prend le descripteur epoll, un tableau de sortie de epoll_event, sa capacité et un délai d’attente en millisecondes.
La fonction se bloque jusqu’à ce qu’au moins un descripteur soit prêt (ou que le délai d’attente expire), puis renvoie le nombre d’événements placés dans votre tableau. Seules ces entrées sont valides.
struct epoll_event events[MAX_EVENTS];
int n = epoll_wait(epfd, events, MAX_EVENTS, -1);
if (n == -1) { perror("epoll_wait"); }L’argument de délai d’attente
Un délai d’attente de -1 bloque indéfiniment jusqu’à l’arrivée d’un événement. La valeur 0 renvoie immédiatement, en interrogeant l’état sans attendre.
Une valeur positive limite la durée d’attente. C’est pratique si vous devez également exécuter des tâches périodiques, comme vider les journaux ou supprimer les connexions inactives arrivées à expiration.
int n = epoll_wait(epfd, events, MAX_EVENTS, 1000); /* up to 1s */
if (n == 0) run_periodic_tasks();Dimensionner le tableau d’événements
La capacité du tableau limite le nombre d’événements prêts qu’un appel à epoll_wait() peut signaler. S’il y en a davantage, le reste est renvoyé lors de l’appel suivant.
Une taille raisonnable, comme 64 ou 1024, convient très bien : epoll répartit équitablement les événements en attente entre les appels, de sorte qu’aucun ne soit privé de traitement.
#define MAX_EVENTS 64
struct epoll_event events[MAX_EVENTS];Distribuer le travail sur la socket d’écoute
Dans la boucle, vérifiez d’abord si le descripteur prêt est votre socket d’écoute. Si c’est le cas, cela signifie que de nouvelles connexions sont en attente.
Vous les acceptez, les configurez en mode non bloquant et les enregistrez auprès d’epoll. Tous les autres descripteurs correspondent à des clients existants qui ont des données à lire ou de la place disponible pour écrire.
if (events[i].data.fd == listen_fd) {
accept_new_connections(epfd, listen_fd);
} else {
handle_client(epfd, &events[i]);
}Accepter les connexions dans une boucle
Un seul événement de préparation sur la socket d’écoute peut correspondre à plusieurs connexions en attente. Appelez accept() en boucle jusqu’à ce qu’il renvoie EAGAIN.
C’est indispensable en mode à déclenchement par front et constitue une bonne pratique dans tous les cas, afin de ne pas laisser les clients attendre jusqu’au prochain réveil.
for (;;) {
int c = accept(listen_fd, NULL, NULL);
if (c == -1) { if (errno == EAGAIN) break; else break; }
set_nonblocking(c);
add_to_epoll(epfd, c);
}Gérer les événements de lecture
Lorsque EPOLLIN se produit sur un client, lisez les données. En mode à déclenchement par niveau, un seul appel à recv() par réveil est acceptable.
Une valeur de retour égale à 0 signifie que le client a fermé la connexion : libérez ses ressources. Une valeur négative accompagnée de EAGAIN signifie que vous avez actuellement lu toutes les données disponibles.
ssize_t r = recv(fd, buf, sizeof buf, 0);
if (r == 0) { close_conn(epfd, fd); }
else if (r > 0) { process(buf, r); }Vider les données en mode à déclenchement par front
Avec EPOLLET, vous devez lire jusqu’à ce que recv() renvoie EAGAIN. Le noyau ne vous avertit que lors du passage à l’état lisible : les octets restants seraient donc ignorés jusqu’au prochain changement.
Placez la lecture dans une boucle et n’en sortez qu’en cas de EAGAIN ou de fin du flux.
for (;;) {
ssize_t r = recv(fd, buf, sizeof buf, 0);
if (r > 0) process(buf, r);
else if (r == 0) { close_conn(epfd, fd); break; }
else { if (errno == EAGAIN) break; else { close_conn(epfd, fd); break; } }
}Gérer les événements d’écriture
EPOLLOUT se produit lorsqu’une socket peut accepter davantage de données sortantes. Vous ne souhaitez surveiller cet événement que lorsque des octets mis en tampon n’ont pas été entièrement envoyés précédemment.
Videz votre tampon ; lorsqu’il est vide, repassez à EPOLLIN uniquement avec EPOLL_CTL_MOD, afin d’éviter un flux constant de notifications indiquant que la socket est prête à écrire.
if (events[i].events & EPOLLOUT) {
flush_pending(fd);
if (buffer_empty(fd)) watch_read_only(epfd, fd);
}Gérer les attentes interrompues
epoll_wait() peut renvoyer -1 avec errno == EINTR si un signal l’interrompt. Il ne s’agit pas d’une véritable erreur : recommencez simplement.
Les boucles robustes continuent en cas de EINTR et ne traitent comme fatals que les autres codes d’erreur. Oublier ce cas peut faire tomber un serveur dès qu’il reçoit un signal.
int n = epoll_wait(epfd, events, MAX_EVENTS, -1);
if (n == -1) {
if (errno == EINTR) continue;
perror("epoll_wait"); break;
}Une boucle, de nombreux clients
En réunissant ces éléments, un seul thread parcourt indéfiniment les descripteurs prêts : accepter, lire, écrire, fermer, puis recommencer.
Comme le thread dort dans epoll_wait() lorsqu’il n’y a rien à faire et ne traite que le travail prêt lorsqu’il est occupé, cette conception gère facilement des dizaines de milliers de connexions simultanées.
Vérification rapide
Vérifiez votre compréhension de la boucle d’événements.
Récapitulatif
La boucle d’événements appelle epoll_wait(), parcourt les événements prêts et distribue le travail : accepter sur la socket d’écoute, lire avec EPOLLIN, vider le tampon avec EPOLLOUT et nettoyer lors d’un raccrochage.
Effectuez les acceptations et les lectures dans des boucles, recommencez en cas de EINTR et videz complètement les données avec EPOLLET. Nous allons maintenant assembler un serveur d’écho complet.
Questions Fréquemment Posées
La leçon « La boucle d’événements » est-elle gratuite ?
Oui — le texte complet de « La boucle d’événements » est gratuit à lire ici sur le web. Pour la pratiquer de manière interactive (un éditeur de code intégré et un tuteur IA 24/7) et déverrouiller le reste du cours C Academy, passe à CoddyKit PRO. Le cours C Academy comprend 4 leçons au total.
Qu'est-ce que j'apprendrai dans « La boucle d’événements » ?
Réagissez aux sockets lisibles. Tu pratiques C Academy avec du code pratique que tu exécutes directement dans le navigateur, et un tuteur IA 24/7 répond à tes questions au fur et à mesure que tu avances dans la leçon.
Dois-je avoir de l'expérience pour commencer C Academy ?
Aucune expérience préalable n'est requise. C Academy sur CoddyKit est structuré pour les débutants jusqu'aux apprenants avancés, donc tu peux commencer ici ou depuis le début et avancer à ton rythme. Ceci est la leçon 3 sur 4.
Combien de temps prend la leçon « La boucle d’événements » ?
La plupart des leçons CoddyKit prennent environ 5–10 minutes. Chacune est courte et interactive, tu progresses régulièrement et tu repiques exactement où tu t'es arrêté sur le web et l'app.
Peux-tu écrire et exécuter du code dans cette leçon C Academy ?
Oui. Chaque leçon C Academy inclut un éditeur de code intégré, tu écris et exécutes du vrai code directement dans ton navigateur et tu reçois des retours IA instantanés — aucune configuration locale requise.
Toutes les leçons de ce cours
- E/S bloquantes ou non bloquantes
- Configurer epoll
- La boucle d’événements
- Un serveur d’écho simple