selectors no Python: vários sockets

Publicado em: 28/08/2026
Tempo de leitura: 6 minutos
Flat lay of a complete toolset neatly organized in a workshop setting, essential for auto repair tasks.

O módulo selectors oferece uma camada portátil para monitorar vários objetos de I/O e descobrir quais estão prontos para leitura ou escrita. Ele escolhe automaticamente a melhor implementação disponível no sistema, como epoll, kqueue, poll ou select. É útil em servidores, proxies, clientes concorrentes, consoles e protocolos com muitas conexões não bloqueantes.

Um selector não executa operações por você. Ele apenas informa readiness. O programa ainda precisa aceitar conexões, ler parcialmente, manter buffers, lidar com EOF, controlar escrita e aplicar timeouts. Para aplicações maiores, asyncio pode oferecer uma abstração mais completa, mas selectors ensina e resolve a camada fundamental.

Crie o selector padrão

DefaultSelector escolhe a implementação recomendada para a plataforma.

import selectors

selector = selectors.DefaultSelector()

Feche o selector no final para liberar descritores internos.

Registre um socket

O socket deve estar em modo não bloqueante.

import socket

servidor = socket.socket()
servidor.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
servidor.bind(("127.0.0.1", 9000))
servidor.listen()
servidor.setblocking(False)

selector.register(servidor, selectors.EVENT_READ, data=None)

O evento de leitura no socket de escuta significa que uma conexão pode ser aceita.

select

selector.select(timeout) retorna pares de SelectorKey e máscara de eventos.

eventos = selector.select(timeout=1.0)
for chave, mascara in eventos:
    if chave.data is None:
        aceitar(chave.fileobj)
    else:
        atender(chave, mascara)

Um resultado vazio após timeout é normal e permite executar manutenção.

SelectorKey

A chave contém o objeto registrado, descritor, eventos de interesse e dados associados.

Use data para armazenar estado da conexão, como endereço, buffers e fase do protocolo.

Aceite todas as conexões pendentes

Em modo não bloqueante, aceite até receber BlockingIOError.

def aceitar(servidor):
    while True:
        try:
            conexao, endereco = servidor.accept()
        except BlockingIOError:
            break
        conexao.setblocking(False)
        estado = Conexao(endereco=endereco, entrada=bytearray(), saida=bytearray())
        selector.register(conexao, selectors.EVENT_READ, data=estado)

Aceitar apenas uma conexão pode deixar outras aguardando até o próximo ciclo.

Leia sem bloquear

Um evento de leitura indica que a operação provavelmente avançará.

try:
    bloco = sock.recv(4096)
except BlockingIOError:
    return

if bloco:
    estado.entrada.extend(bloco)
else:
    fechar_conexao(sock)

Bytes vazios indicam fechamento ordenado do peer.

TCP é fluxo

Uma leitura não corresponde a uma mensagem. Dados podem chegar divididos ou agrupados.

Implemente framing com delimitador, tamanho prefixado ou formato definido. Consulte socket no Python.

Buffers de entrada

Acumule bytes até existir uma mensagem completa.

estado.entrada.extend(bloco)
while mensagem_completa(estado.entrada):
    mensagem = extrair_mensagem(estado.entrada)
    processar(mensagem, estado)

Defina limite para impedir que um cliente envie dados sem fim sem completar um frame.

Escrita parcial

send() pode escrever apenas parte do buffer.

if estado.saida:
    try:
        enviados = sock.send(estado.saida)
    except BlockingIOError:
        enviados = 0
    del estado.saida[:enviados]

Mantenha os bytes restantes para o próximo evento de escrita.

Registre EVENT_WRITE somente quando necessário

Sockets normalmente aparecem como graváveis quase o tempo todo. Manter EVENT_WRITE sempre ativo pode criar busy loop.

Adicione o interesse quando o buffer de saída ficar não vazio e remova quando ele for drenado.

modify

modify() altera eventos e dados de um registro.

eventos = selectors.EVENT_READ
if estado.saida:
    eventos |= selectors.EVENT_WRITE
selector.modify(sock, eventos, data=estado)

Centralize essa atualização para manter a máscara consistente.

unregister e close

Remova o objeto do selector antes de fechá-lo.

def fechar_conexao(sock):
    try:
        selector.unregister(sock)
    except Exception:
        pass
    sock.close()

Capture apenas erros esperados em código real e registre estado inconsistente.

Exceções de conexão

Reset, broken pipe e erros de rede podem surgir durante leitura ou escrita.

Feche a conexão individual e mantenha o loop principal ativo, a menos que o erro afete o selector ou socket de escuta.

Timeouts por conexão

O timeout de select() não é automaticamente um timeout de protocolo.

Registre a última atividade em cada estado e feche conexões que excederem o prazo.

agora = time.monotonic()
for chave in list(selector.get_map().values()):
    estado = chave.data
    if estado and agora - estado.ultima_atividade > 30:
        fechar_conexao(chave.fileobj)

Use relógio monotônico

Deadlines e durações devem usar time.monotonic().

Ajustes no relógio civil não devem prolongar ou encurtar timeouts de conexão.

Backpressure

Se o peer lê devagar, o buffer de saída pode crescer.

Defina um limite, pause a leitura de novas requisições, descarte trabalho ou feche a conexão. Nunca permita crescimento ilimitado.

Fairness

Um cliente com grande volume não deve monopolizar o loop.

Limite bytes, mensagens ou tempo processado por conexão em cada ciclo e volte ao selector.

Trabalho bloqueante

Banco de dados, filesystem lento e CPU pesada bloqueiam todas as conexões se executados no loop.

Envie trabalho a um pool limitado ou use uma arquitetura assíncrona. Ao receber o resultado, acorde o loop de forma segura.

Acorde o selector

Uma thread externa pode precisar notificar o loop. Um padrão portátil usa socketpair() quando disponível ou um pipe compatível.

Registre a ponta de leitura; a thread escreve um byte para interromper a espera.

Threads

Mantenha registros, modificações e fechamento preferencialmente na thread do loop.

Outras threads devem enviar comandos por uma fila e acordar o selector, reduzindo condições de corrida.

Integração com queue

Uma queue.Queue pode transportar comandos do worker para o loop.

O próximo artigo detalhará filas sincronizadas, shutdown e backpressure.

TLS

Sockets TLS não bloqueantes podem precisar alternar entre leitura e escrita durante handshake ou operações.

Trate exceções como SSLWantReadError e SSLWantWriteError ajustando a máscara. Para produção, uma biblioteca de alto nível reduz a complexidade.

Limitações no Windows

A capacidade de selecionar objetos diferentes de sockets varia. Não presuma que pipes e arquivos comuns funcionam como em Unix.

Teste na plataforma de destino e use APIs apropriadas ao Windows quando necessário.

Arquivos regulares

Arquivos comuns geralmente são considerados sempre prontos e não ganham muito com selectors.

Use leitura em chunks ou threads para I/O de arquivo quando necessário.

Sinais e interrupções

A espera pode ser interrompida por sinais conforme plataforma e versão.

Mantenha handlers mínimos e use uma flag ou mecanismo de wakeup para shutdown.

Encerramento gracioso

Pare de aceitar novas conexões, marque conexões existentes para fechar após drenar buffers, aplique deadline e finalize restantes.

Feche registros, socket de escuta, mecanismo de wakeup e selector.

Observabilidade

Registre conexões ativas, bytes, mensagens, tamanho de buffers, duração, timeouts e erros.

Métricas de fila de saída e tempo no loop revelam backpressure e callbacks bloqueantes.

Segurança

Limite conexões por origem, tamanho de frame, buffer, tempo ocioso e taxa de mensagens.

Valide protocolo antes de alocar memória e não use endereço IP sozinho como identidade.

Testes

Teste leitura parcial, escrita parcial, vários frames juntos, EOF, reset, cliente lento, buffers cheios, timeout, wakeup, shutdown, IPv6 e Windows.

Use sockets locais e deadlines amplos em vez de sleeps exatos.

Erros comuns

Os erros mais frequentes são manter EVENT_WRITE sempre ativo, presumir mensagens completas, ignorar send parcial, executar trabalho bloqueante no loop, esquecer unregister, não limitar buffers e modificar o selector concorrentemente sem protocolo.

Conclusão

selectors fornece multiplexação portátil para muitos sockets não bloqueantes. Registre interesses mínimos, mantenha buffers por conexão, aplique timeouts e backpressure e faça cleanup explícito.

Consulte a documentação oficial de selectors, socket no Python e select no Python.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Conteúdo do artigo

    Artigos relacionados

    Close-up view of a computer screen displaying code in a software development environment.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    contextvars no Python: contexto assíncrono

    Aprenda contextvars no Python para contexto por task, request IDs, logging, copy_context, propagação a threads e restauração segura com tokens.

    Ler mais

    Tempo de leitura: 6 minutos
    28/08/2026
    Person writing appointments on a calendar with a blue pen. High angle view.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    sched no Python: agende eventos

    Aprenda sched no Python para agendar eventos, usar prioridades, cancelar tarefas, criar recorrência sem drift e integrar executores.

    Ler mais

    Tempo de leitura: 6 minutos
    28/08/2026
    Close-up of a vibrant yellow python coiled with textured scales in vibrant light.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    bisect no Python: listas ordenadas

    Aprenda bisect no Python para busca binária, inserção ordenada, duplicatas, funções key, faixas, rankings e sincronização segura.

    Ler mais

    Tempo de leitura: 5 minutos
    28/08/2026
    A developer typing code on a laptop with a Python book beside in an office.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    heapq no Python: filas de prioridade

    Aprenda heapq no Python para filas de prioridade, top-k, merge, empates, atualização de prioridades, lazy deletion e backpressure.

    Ler mais

    Tempo de leitura: 5 minutos
    28/08/2026
    A detailed close-up of a sleek computer keyboard with numerical keypad.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    array no Python: números compactos

    Aprenda array no Python para armazenar números compactos, trabalhar com typecodes, bytes, arquivos, memoryview e validação portátil.

    Ler mais

    Tempo de leitura: 5 minutos
    28/08/2026
    Chic portrait of a woman wearing trendy sunglasses reflecting numbers, captured in a modern setting.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    struct no Python: dados binários

    Aprenda struct no Python para empacotar dados binários, controlar endianness, offsets, padding, buffers, sockets e validação segura.

    Ler mais

    Tempo de leitura: 5 minutos
    28/08/2026