selectors en Python: varios sockets

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

El módulo selectors ofrece una capa portable para monitorizar varios objetos de I/O y descubrir cuáles están listos para lectura o escritura. Selecciona automáticamente una implementación eficiente del sistema, como epoll, kqueue, poll o select. Es útil en servidores, proxies, clientes concurrentes y protocolos con muchas conexiones no bloqueantes.

Un selector no realiza el I/O por ti. Solo informa readiness. El programa todavía debe aceptar conexiones, manejar lecturas y escrituras parciales, mantener buffers, detectar EOF y aplicar timeouts. Para sistemas mayores, asyncio ofrece una abstracción más completa, pero selectors muestra la capa fundamental.

Crea el selector predeterminado

DefaultSelector elige la implementación recomendada para la plataforma.

import selectors

selector = selectors.DefaultSelector()

Ciérralo durante shutdown para liberar descriptores internos.

Registra un socket

El socket debe ser no 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)

Read readiness en un socket de escucha significa que probablemente puede aceptarse una conexión.

select

selector.select(timeout) devuelve pares de SelectorKey y máscara de eventos.

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

Un resultado vacío tras timeout es normal y permite mantenimiento.

SelectorKey

La clave contiene el objeto registrado, descriptor, eventos solicitados y datos asociados.

Usa data para guardar dirección, buffers y fase del protocolo.

Acepta todas las conexiones pendientes

En modo no bloqueante, acepta hasta recibir BlockingIOError.

def aceptar(servidor):
    while True:
        try:
            conexion, direccion = servidor.accept()
        except BlockingIOError:
            break
        conexion.setblocking(False)
        estado = Conexion(direccion=direccion, entrada=bytearray(), salida=bytearray())
        selector.register(conexion, selectors.EVENT_READ, data=estado)

Aceptar solo una puede dejar otras esperando otro ciclo.

Lee sin bloquear

Un evento de lectura indica que la operación probablemente avanzará.

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

if bloque:
    estado.entrada.extend(bloque)
else:
    cerrar_conexion(sock)

Bytes vacíos indican cierre ordenado del peer.

TCP es un flujo

Una lectura no equivale a un mensaje. Los datos pueden llegar divididos o agrupados.

Implementa framing con delimitador, prefijo de longitud o formato documentado. Consulta socket en Python.

Buffers de entrada

Acumula bytes hasta tener un frame completo.

estado.entrada.extend(bloque)
while mensaje_completo(estado.entrada):
    mensaje = extraer_mensaje(estado.entrada)
    procesar(mensaje, estado)

Define un límite para impedir datos infinitos sin completar un frame.

Escritura parcial

send() puede escribir solo parte del buffer.

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

Conserva los bytes restantes para el próximo evento.

Registra EVENT_WRITE solo cuando haga falta

Los sockets suelen aparecer como escribibles casi siempre. Mantener write interest activo puede crear busy loop.

Añádelo cuando el buffer de salida tenga datos y elimínalo al vaciarse.

modify

modify() cambia eventos y datos asociados.

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

Centraliza el cálculo de la máscara.

unregister y close

Retira el objeto del selector antes de cerrarlo.

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

En producción, captura errores esperados específicamente.

Errores de conexión

Reset, broken pipe y fallos de red pueden surgir durante lectura o escritura.

Cierra la conexión afectada y conserva el loop salvo que falle el socket de escucha o el selector.

Timeouts por conexión

El timeout de select() no crea automáticamente un timeout de protocolo.

Guarda la última actividad y cierra clientes inactivos.

ahora = time.monotonic()
for clave in list(selector.get_map().values()):
    estado = clave.data
    if estado and ahora - estado.ultima_actividad > 30:
        cerrar_conexion(clave.fileobj)

Reloj monotónico

Deadlines y duraciones deben usar time.monotonic().

Los ajustes del reloj civil no deberían cambiar timeouts.

Backpressure

Si el peer lee lentamente, el buffer de salida puede crecer.

Define límite, pausa requests nuevas, rechaza trabajo o cierra la conexión. Nunca permitas crecimiento ilimitado.

Fairness

Un cliente con mucho volumen no debe monopolizar el loop.

Limita bytes, mensajes o tiempo por conexión en cada ciclo.

Trabajo bloqueante

Base de datos, filesystem lento y CPU pesada bloquean todas las conexiones si se ejecutan en el loop.

Envía el trabajo a un pool limitado o usa arquitectura async. Despierta el loop cuando llegue el resultado.

Despierta el selector

Una thread externa puede notificar al loop mediante socketpair() o un pipe apropiado.

Registra la punta de lectura y escribe un byte para interrumpir la espera.

Threads

Realiza register, modify y close preferiblemente en la thread del loop.

Otras threads pueden enviar comandos por queue y activar el wakeup.

Integración con queue

Una queue.Queue puede transportar comandos de workers al loop.

El siguiente artículo detallará colas sincronizadas y shutdown.

TLS

Los sockets TLS no bloqueantes pueden alternar entre lectura y escritura durante handshake y operaciones.

Gestiona SSLWantReadError y SSLWantWriteError ajustando la máscara. Un framework reduce complejidad.

Limitaciones en Windows

La posibilidad de seleccionar objetos distintos de sockets varía en Windows. Pipes y archivos comunes no funcionan igual que en Unix.

Prueba la plataforma objetivo.

Archivos regulares

Los archivos comunes suelen considerarse siempre listos y ganan poco con selectors.

Usa lectura por chunks o threads para I/O de archivos.

Señales

La espera puede ser interrumpida por signals según sistema y versión.

Mantén handlers mínimos y usa una flag o wakeup para shutdown.

Cierre graceful

Deja de aceptar conexiones, permite drenar buffers, aplica deadline y cierra las restantes.

Cierra registros, socket de escucha, wakeup y selector.

Observabilidad

Registra conexiones activas, bytes, mensajes, tamaños de buffers, duración, timeouts y errores.

Métricas de salida y latencia del loop revelan backpressure y callbacks bloqueantes.

Seguridad

Limita conexiones por origen, tamaño de frame, buffer, inactividad y tasa de mensajes.

Valida campos antes de reservar memoria y no uses IP como única identidad.

Pruebas

Prueba lectura parcial, escritura parcial, varios frames juntos, EOF, reset, cliente lento, buffers llenos, timeout, wakeup, shutdown, IPv6 y Windows.

Usa sockets locales y deadlines amplios.

Errores comunes

Los fallos frecuentes son mantener EVENT_WRITE siempre activo, asumir mensajes completos, ignorar send parcial, ejecutar trabajo bloqueante, olvidar unregister, no limitar buffers y modificar el selector desde varias threads sin protocolo.

Conclusión

selectors ofrece multiplexación portable para muchos sockets no bloqueantes. Registra solo intereses necesarios, mantiene buffers por conexión, aplica timeouts y backpressure y limpia recursos explícitamente.

Consulta la documentación oficial de selectors, socket en Python y select en Python.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Contenido del artículo

    Artículos relacionados

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

    tracemalloc en Python: rastrea memoria

    Aprende tracemalloc en Python para medir picos, crear y comparar snapshots, filtrar asignaciones y diagnosticar crecimiento de memoria.

    Ler mais

    Tempo de leitura: 6 minutos
    28/08/2026
    Detailed shot of a Jungle Carpet Python (Morelia spilota cheynei) in its natural habitat.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    queue en Python: comunica threads

    Aprende queue en Python para comunicar threads con FIFO, LIFO, prioridad, backpressure, task tracking, sentinelas y shutdown seguro.

    Ler mais

    Tempo de leitura: 5 minutos
    28/08/2026
    Detailed close-up texture of a snake's patterned skin showcasing natural patterns and scales.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    contextvars en Python: contexto async

    Aprende contextvars en Python para contexto por task, request IDs, logging, copy_context, propagación a threads y restauración con tokens.

    Ler mais

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

    sched en Python: programa eventos

    Aprende sched en Python para programar eventos, usar prioridades, cancelar tareas, crear recurrencia sin drift e integrar executors.

    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 Avanzado
    Foto de perfil de Leandro Hirt da Academify

    bisect en Python: listas ordenadas

    Aprende bisect en Python para búsqueda binaria, inserción ordenada, duplicados, funciones key, rangos, rankings y concurrencia segura.

    Ler mais

    Tempo de leitura: 5 minutos
    28/08/2026
    Crop unrecognizable person in gray sweater applying glue stick on paper while forming photo album on floor
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    heapq en Python: colas de prioridad

    Aprende heapq en Python para colas de prioridad, top-k, merge, empates estables, actualización de prioridades, lazy deletion y backpressure.

    Ler mais

    Tempo de leitura: 4 minutos
    28/08/2026