Il ciclo di eventi
Reagisca ai socket pronti per la lettura.
Il ciclo di eventi è una lezione C Academy gratuita su CoddyKit. Questa è la lezione 3 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento C Academy, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso C Academy include 4 lezioni in totale.
Il cuore del server
Il ciclo degli eventi è il motore di un server epoll. Attende ripetutamente i descrittori pronti e assegna il lavoro ai relativi gestori.
La struttura è sempre la stessa: chiamare epoll_wait(), scorrere gli eventi restituiti, agire su ciascuno e poi ricominciare. Tutto il resto si appoggia a questa struttura di base.
for (;;) {
int n = epoll_wait(epfd, events, MAX_EVENTS, -1);
for (int i = 0; i < n; i++) handle(&events[i]);
}Chiamata a epoll_wait
epoll_wait() accetta il fd di epoll, un array di output di epoll_event, la sua capacità e un timeout espresso in millisecondi.
Rimane in attesa finché almeno un descrittore non è pronto (oppure finché scade il timeout) e restituisce il numero di eventi inseriti nell'array. Solo quelle voci sono valide.
struct epoll_event events[MAX_EVENTS];
int n = epoll_wait(epfd, events, MAX_EVENTS, -1);
if (n == -1) { perror("epoll_wait"); }L'argomento timeout
Un timeout pari a -1 blocca indefinitamente finché non arriva un evento. Il valore 0 restituisce immediatamente, eseguendo il polling senza sospendersi.
Un valore positivo limita la durata dell'attesa; è utile se deve anche eseguire attività periodiche, come svuotare i log o chiudere le connessioni inattive.
int n = epoll_wait(epfd, events, MAX_EVENTS, 1000); /* up to 1s */
if (n == 0) run_periodic_tasks();Dimensionamento dell'array degli eventi
La capacità dell'array limita il numero di eventi pronti che una singola chiamata a epoll_wait() può segnalare. Se ce ne sono altri pronti, vengono restituiti nella chiamata successiva.
Una dimensione moderata, come 64 o 1024, è sufficiente; epoll alterna equamente gli eventi in attesa tra le varie chiamate, quindi nessuno rimane escluso.
#define MAX_EVENTS 64
struct epoll_event events[MAX_EVENTS];Distribuzione del lavoro sul socket in ascolto
All'interno del ciclo, verifichi innanzitutto se il descrittore pronto è il socket in ascolto. In tal caso significa che ci sono nuove connessioni in attesa.
Le accetti, le imposti in modalità non bloccante e le registri con epoll. Tutti gli altri descrittori corrispondono a client già connessi, con dati da leggere o spazio disponibile per scrivere.
if (events[i].data.fd == listen_fd) {
accept_new_connections(epfd, listen_fd);
} else {
handle_client(epfd, &events[i]);
}Accettazione delle connessioni in un ciclo
Un singolo evento di disponibilità sul socket in ascolto può indicare diverse connessioni in attesa. Ripeta accept() finché non restituisce EAGAIN.
Questo è essenziale in modalità edge-triggered e costituisce comunque una buona pratica, così non lascia i client in attesa fino alla successiva riattivazione.
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);
}Gestione degli eventi di lettura
Quando EPOLLIN si verifica su un client, legga i dati dal socket. In modalità level-triggered, una singola chiamata a recv() per ogni riattivazione è accettabile.
Un valore restituito pari a 0 significa che il client ha chiuso la connessione: esegua la pulizia necessaria. Un valore negativo con EAGAIN significa che, per il momento, ha letto tutti i dati disponibili.
ssize_t r = recv(fd, buf, sizeof buf, 0);
if (r == 0) { close_conn(epfd, fd); }
else if (r > 0) { process(buf, r); }Svuotamento in modalità edge-triggered
Con EPOLLET deve leggere finché recv() non restituisce EAGAIN. Il kernel notifica solo il passaggio allo stato leggibile, quindi i byte rimasti non verrebbero rilevati fino al successivo cambiamento.
Inserisca la lettura in un ciclo e ne esca solo con EAGAIN o alla fine del flusso.
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; } }
}Gestione degli eventi di scrittura
EPOLLOUT si verifica quando un socket può accettare altri dati in uscita. Le serve solo quando ha byte nel buffer che in precedenza non sono stati inviati completamente.
Svuoti il buffer; quando è vuoto, ripristini l'interesse al solo EPOLLIN con EPOLL_CTL_MOD, per evitare un flusso continuo di notifiche di disponibilità alla scrittura.
if (events[i].events & EPOLLOUT) {
flush_pending(fd);
if (buffer_empty(fd)) watch_read_only(epfd, fd);
}Gestione delle attese interrotte
epoll_wait() può restituire -1 con errno == EINTR se un segnale la interrompe. Non è un vero errore: riprovi semplicemente.
I cicli robusti continuano quando ricevono EINTR e considerano fatali solo gli altri codici di errore. Dimenticarlo può causare l'arresto del server alla prima ricezione di un segnale.
int n = epoll_wait(epfd, events, MAX_EVENTS, -1);
if (n == -1) {
if (errno == EINTR) continue;
perror("epoll_wait"); break;
}Un ciclo, molti client
Unendo tutti i componenti, un singolo thread scorre continuamente i descrittori pronti: accetta, legge, scrive, chiude e ripete.
Poiché il thread rimane in attesa in epoll_wait() quando non c'è lavoro e gestisce solo le attività pronte quando è occupato, questa architettura gestisce senza difficoltà decine di migliaia di connessioni simultanee.
Verifica rapida
Verifichi la Sua comprensione del ciclo degli eventi.
Riepilogo
Il ciclo degli eventi chiama epoll_wait(), scorre gli eventi pronti e distribuisce il lavoro: accetta le connessioni sul socket in ascolto, legge con EPOLLIN, svuota il buffer con EPOLLOUT ed esegue la pulizia in caso di chiusura.
Ripeta in un ciclo le operazioni di accettazione e lettura, riprovi in caso di EINTR e svuoti completamente i buffer con EPOLLET. Nel prossimo passaggio assembleremo un echo server completo.
Impara C con un tutor IA — gratis
Scrivi ed esegui vero codice nel tuo browser, ricevi aiuto istantaneo da un tutor IA disponibile 24/7, e riprendi da dove hai lasciato sul web o nell'app.
- Corsi
- 39
- Lezioni
- 144
Domande Frequenti
La lezione «Il ciclo di eventi» è gratuita?
Sì — il testo completo di «Il ciclo di eventi» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso C Academy, passa a CoddyKit PRO. Il corso C Academy include 4 lezioni in totale.
Cosa imparerò in «Il ciclo di eventi»?
Reagisca ai socket pronti per la lettura. Eserciti C Academy con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.
Ho bisogno di esperienza per iniziare C Academy?
Non è richiesta alcuna esperienza precedente. C Academy su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 3 di 4.
Quanto tempo richiede la lezione «Il ciclo di eventi»?
La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.
Posso scrivere ed eseguire codice in questa lezione C Academy?
Sì. Ogni lezione C Academy include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.
Tutte le lezioni di questo corso
- I/O bloccante e non bloccante
- Configurare epoll
- Il ciclo di eventi
- Un semplice server echo