O laço de eventos
Reaja a sockets legíveis.
O laço de eventos é uma aula grátis de C Academy no CoddyKit. Esta é a aula 3 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de C Academy, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de C Academy inclui 4 aulas no total.
O coração do servidor
O loop de eventos é o mecanismo que movimenta um servidor epoll. Ele espera repetidamente por descritores prontos e encaminha o trabalho aos manipuladores.
Sua estrutura é sempre a mesma: chamar epoll_wait(), percorrer os eventos retornados, agir sobre cada um e repetir o loop. Todo o restante se apoia nesse esqueleto.
for (;;) {
int n = epoll_wait(epfd, events, MAX_EVENTS, -1);
for (int i = 0; i < n; i++) handle(&events[i]);
}Chamando epoll_wait
epoll_wait() recebe o fd de epoll, uma matriz de saída de epoll_event, sua capacidade e um tempo limite em milissegundos.
Ela bloqueia até que pelo menos um descritor esteja pronto (ou até que o tempo limite expire) e retorna quantos eventos foram preenchidos na sua matriz. Somente essas entradas são válidas.
struct epoll_event events[MAX_EVENTS];
int n = epoll_wait(epfd, events, MAX_EVENTS, -1);
if (n == -1) { perror("epoll_wait"); }O argumento de tempo limite
Um tempo limite de -1 bloqueia indefinidamente até a chegada de um evento. Um valor de 0 retorna imediatamente, consultando o estado sem aguardar.
Um valor positivo limita o tempo de espera, o que é útil quando você também precisa executar tarefas periódicas, como liberar registros ou expirar conexões ociosas.
int n = epoll_wait(epfd, events, MAX_EVENTS, 1000); /* up to 1s */
if (n == 0) run_periodic_tasks();Definindo o tamanho da matriz de eventos
A capacidade da matriz limita quantos eventos prontos uma chamada a epoll_wait() pode informar. Se houver mais eventos prontos, o restante será retornado na chamada seguinte.
Um tamanho moderado, como 64 ou 1024, é suficiente; epoll alterna de maneira justa entre os eventos pendentes nas chamadas, portanto nenhum fica sem atendimento.
#define MAX_EVENTS 64
struct epoll_event events[MAX_EVENTS];Encaminhando eventos no soquete de escuta
Dentro do loop, verifique primeiro se o descritor pronto é o seu soquete de escuta. Se for, isso significa que há novas conexões pendentes.
Você as aceita, configura-as como não bloqueantes e as registra no epoll. Todos os outros descritores correspondem a clientes existentes com dados para ler ou espaço para escrever.
if (events[i].data.fd == listen_fd) {
accept_new_connections(epfd, listen_fd);
} else {
handle_client(epfd, &events[i]);
}Aceitando conexões em um loop
Um único evento de prontidão no soquete de escuta pode significar que há várias conexões pendentes. Repita accept() até que ela retorne EAGAIN.
Isso é essencial no modo orientado por borda e uma boa prática em qualquer situação, para que você não deixe clientes esperando até a próxima ativação.
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);
}Processando eventos de leitura
Quando EPOLLIN ocorrer em um cliente, leia os dados dele. No modo orientado por nível, uma única chamada a recv() por ativação é aceitável.
Um retorno de 0 significa que o cliente fechou a conexão: faça a limpeza. Um retorno negativo com EAGAIN significa que você esgotou todos os dados disponíveis no momento.
ssize_t r = recv(fd, buf, sizeof buf, 0);
if (r == 0) { close_conn(epfd, fd); }
else if (r > 0) { process(buf, r); }Esvaziando no modo orientado por borda
Com EPOLLET, você deve ler até que recv() retorne EAGAIN. O kernel só o notifica na transição para o estado legível, portanto os bytes restantes seriam perdidos até a próxima mudança.
Coloque a leitura em um loop e saia dele somente ao receber EAGAIN ou ao chegar ao fim do fluxo.
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; } }
}Processando eventos de escrita
EPOLLOUT ocorre quando um soquete pode aceitar mais dados de saída. Você só precisa desse evento quando houver bytes armazenados em buffer que não tenham sido enviados completamente antes.
Libere o conteúdo do buffer; quando ele estiver vazio, altere o interesse de volta para somente EPOLLIN usando EPOLL_CTL_MOD, para evitar um fluxo constante de eventos indicando prontidão para escrita.
if (events[i].events & EPOLLOUT) {
flush_pending(fd);
if (buffer_empty(fd)) watch_read_only(epfd, fd);
}Processando esperas interrompidas
epoll_wait() pode retornar -1 com errno == EINTR se um sinal a interromper. Isso não é um erro real; basta tentar novamente.
Loops robustos continuam ao receber EINTR e só tratam outros códigos de erro como fatais. Esquecer isso pode encerrar um servidor na primeira vez que ele receber um sinal.
int n = epoll_wait(epfd, events, MAX_EVENTS, -1);
if (n == -1) {
if (errno == EINTR) continue;
perror("epoll_wait"); break;
}Um loop, muitos clientes
Juntando tudo, uma única thread percorre indefinidamente os descritores prontos: aceita, lê, escreve, fecha e repete.
Como a thread dorme em epoll_wait() quando está ociosa e processa apenas o trabalho pronto quando está ocupada, esse design lida confortavelmente com dezenas de milhares de conexões simultâneas.
Verificação rápida
Teste sua compreensão do loop de eventos.
Recapitulação
O loop de eventos chama epoll_wait(), percorre os eventos prontos e encaminha as ações: aceitar no soquete de escuta, ler em EPOLLIN, esvaziar o buffer em EPOLLOUT e fazer a limpeza em caso de desligamento.
Repita suas aceitações e leituras em loops, tente novamente ao receber EINTR e esvazie completamente os dados sob EPOLLET. Em seguida, montaremos um servidor de eco completo.
Perguntas Frequentes
A aula “O laço de eventos” é grátis?
Sim — o texto completo de “O laço de eventos” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de C Academy, atualize para CoddyKit PRO. O curso de C Academy inclui 4 aulas no total.
O que vou aprender em “O laço de eventos”?
Reaja a sockets legíveis. Você pratica C Academy com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.
Preciso ter experiência prévia para começar C Academy?
Nenhuma experiência prévia é necessária. C Academy no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 3 de 4.
Quanto tempo leva a aula “O laço de eventos”?
A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.
Posso escrever e executar código nesta aula de C Academy?
Sim. Cada aula de C Academy inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.