Python selectors: Many Sockets

Published on: August 28, 2026
Reading time: 5 minutes
A vertical macro shot showcasing metallic socket wrenches in a shallow focus arrangement.

The selectors module provides a portable layer for monitoring many I/O objects and discovering which ones are ready for reading or writing. It selects an efficient implementation available on the operating system, such as epoll, kqueue, poll, or select. It is useful for servers, proxies, concurrent clients, consoles, and protocols with many nonblocking connections.

A selector does not perform I/O for you. It only reports readiness. The program must still accept connections, handle partial reads and writes, maintain buffers, detect EOF, and enforce timeouts. For larger systems, asyncio may provide a richer abstraction, but selectors exposes the essential event-loop layer.

Create the default selector

DefaultSelector chooses the recommended implementation for the platform.

import selectors

selector = selectors.DefaultSelector()

Close it during shutdown to release internal descriptors.

Register a listening socket

The socket must be nonblocking.

import socket

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

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

Read readiness on a listening socket means at least one connection can probably be accepted.

Call select

selector.select(timeout) returns pairs containing a SelectorKey and an event mask.

events = selector.select(timeout=1.0)
for key, mask in events:
    if key.data is None:
        accept(key.fileobj)
    else:
        service(key, mask)

An empty result after the timeout is normal and gives the loop a chance to perform maintenance.

SelectorKey

The key contains the registered object, descriptor, requested events, and associated data.

Use data for connection state such as the remote address, input buffer, output buffer, and protocol phase.

Accept every pending connection

In nonblocking mode, accept until BlockingIOError.

def accept(server):
    while True:
        try:
            connection, address = server.accept()
        except BlockingIOError:
            break
        connection.setblocking(False)
        state = Connection(address=address, incoming=bytearray(), outgoing=bytearray())
        selector.register(connection, selectors.EVENT_READ, data=state)

Accepting only once may leave other ready clients waiting for another event-loop cycle.

Read without blocking

A read event means the operation is likely to make progress.

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

if block:
    state.incoming.extend(block)
else:
    close_connection(sock)

An empty byte string indicates an orderly peer shutdown.

TCP is a stream

One read does not equal one application message. Data may arrive split or combined.

Implement framing with a delimiter, length prefix, or documented format. See Python socket.

Input buffers

Accumulate bytes until a complete frame is available.

state.incoming.extend(block)
while message_is_complete(state.incoming):
    message = extract_message(state.incoming)
    process(message, state)

Set a maximum buffer size so a client cannot send endless data without completing a frame.

Partial writes

send() may write only part of the output buffer.

if state.outgoing:
    try:
        sent = sock.send(state.outgoing)
    except BlockingIOError:
        sent = 0
    del state.outgoing[:sent]

Keep remaining bytes for the next write event.

Register EVENT_WRITE only when needed

Sockets are often reported writable most of the time. Keeping write interest enabled permanently can create a busy loop.

Add it when the output buffer becomes nonempty and remove it after the buffer drains.

modify

modify() changes the interest mask and associated data.

events = selectors.EVENT_READ
if state.outgoing:
    events |= selectors.EVENT_WRITE
selector.modify(sock, events, data=state)

Centralize mask calculation so state remains consistent.

unregister and close

Remove an object from the selector before closing it.

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

Production code should catch expected errors specifically and log inconsistent state.

Connection errors

Reset, broken pipe, and network failures can occur during read or write.

Close the affected connection and keep the event loop alive unless the failure affects the listening socket or selector itself.

Per-connection deadlines

The timeout passed to select() is not automatically a protocol timeout.

Track last activity in each connection state and close idle clients after a deadline.

now = time.monotonic()
for key in list(selector.get_map().values()):
    state = key.data
    if state and now - state.last_activity > 30:
        close_connection(key.fileobj)

Use a monotonic clock

Deadlines and durations should use time.monotonic().

Wall-clock adjustments must not extend or shorten network timeouts.

Backpressure

If a peer reads slowly, the output buffer can grow.

Set a limit, pause new request processing, reject work, or close the connection. Never allow unbounded growth.

Fairness

One high-volume client should not monopolize the loop.

Limit bytes, messages, or processing time per connection during each cycle, then return to the selector.

Blocking work

Slow database calls, filesystem operations, and CPU-heavy functions block every connection when run inside the event loop.

Submit them to a bounded pool or use an asynchronous architecture. Wake the loop safely when results become available.

Wake up the selector

An external thread may need to notify the loop. A portable pattern uses socketpair() where available, or a suitable pipe.

Register the read end and write one byte from another thread to interrupt the wait.

Threads

Prefer to perform registration, modification, and closure in the event-loop thread.

Other threads can submit commands through a queue and trigger the wakeup mechanism, reducing races.

Integration with queue

A queue.Queue can transfer commands from workers to the event loop.

The next guide covers synchronized queues, shutdown, and backpressure.

TLS

Nonblocking TLS sockets may alternate between wanting reads and writes during handshakes and normal operations.

Handle SSLWantReadError and SSLWantWriteError by adjusting the interest mask. A high-level framework reduces this complexity for production services.

Windows limitations

Selectability of non-socket objects differs on Windows. Pipes and ordinary files may not behave as they do on Unix.

Test the target platform and use Windows-specific APIs when required.

Regular files

Ordinary files are generally reported ready and gain little from readiness multiplexing.

Use buffered chunk reading or worker threads for file I/O.

Signals and interruptions

Waits may be interrupted by signals depending on platform and version.

Keep signal handlers minimal and use a flag or wakeup mechanism for shutdown.

Graceful shutdown

Stop accepting new connections, allow existing clients to drain output buffers, apply a deadline, and close remaining connections.

Close registrations, the listening socket, wakeup objects, and the selector.

Observability

Record active connections, byte counts, messages, buffer sizes, duration, timeouts, and error classes.

Output-buffer and loop-latency metrics reveal backpressure and blocking callbacks.

Security

Limit connections per source, frame size, buffer size, idle time, and message rate.

Validate protocol fields before allocating memory and do not use IP address alone as identity.

Testing

Test partial reads, partial writes, several frames in one read, EOF, reset, slow clients, full buffers, timeout, wakeup, shutdown, IPv6, and Windows.

Use local sockets and generous deadlines instead of exact sleeps.

Common mistakes

Common failures include keeping EVENT_WRITE enabled permanently, assuming complete messages, ignoring partial sends, running blocking work in the loop, forgetting to unregister, allowing unlimited buffers, and modifying the selector concurrently without a protocol.

Conclusion

selectors provides portable multiplexing for many nonblocking sockets. Register only required interests, maintain per-connection buffers, enforce deadlines and backpressure, and clean up explicitly.

Consult the official selectors documentation, Python socket, and Python select.

Share:

Facebook
WhatsApp
Twitter
LinkedIn

Article content

    Related articles

    A person typing on a laptop with a Python programming book visible, capturing technology and learning.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python tracemalloc: Track Memory

    Learn Python tracemalloc to measure peaks, create and compare snapshots, filter allocations, and diagnose memory growth and leaks.

    Ler mais

    Tempo de leitura: 5 minutos
    28/08/2026
    Commuters line up at a subway station platform, showcasing public transportation dynamics.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python queue: Communicate Between Threads

    Learn Python queue for thread communication with FIFO, LIFO, priority, backpressure, task tracking, sentinels, and safe shutdown.

    Ler mais

    Tempo de leitura: 5 minutos
    28/08/2026
    Vivid close-up of a green tree python coiled on a branch in the rainforest.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python contextvars: Async Context

    Learn Python contextvars for task-local context, request IDs, logging, copy_context, thread propagation, and safe token restoration.

    Ler mais

    Tempo de leitura: 5 minutos
    28/08/2026
    A minimalist June calendar page adorned with red hearts for a romantic touch.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python sched: Schedule Events

    Learn Python sched to schedule events, use priorities, cancel work, create drift-free recurrence, test with fake clocks, and integrate executors.

    Ler mais

    Tempo de leitura: 5 minutos
    28/08/2026
    Stack of colorful sticky notes with a 'To Do' note on top, isolated on white.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python bisect: Keep Lists Sorted

    Learn Python bisect for binary search, ordered insertion, duplicates, key functions, ranges, rankings, and safe concurrent updates.

    Ler mais

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

    Python heapq: Priority Queues

    Learn Python heapq for priority queues, top-k selection, sorted merges, stable ties, lazy deletion, updates, and backpressure.

    Ler mais

    Tempo de leitura: 4 minutos
    28/08/2026