selectors provides portable, efficient event notification for file objects and sockets. Instead of repeatedly checking every socket, an operating-system notification system reports which registered sockets are ready. section 3.4
The core workflow is small:
- Create a DefaultSelector, which chooses an efficient platform-specific implementation. section 3.4
- Register sockets with the read or write events to monitor; deregister them when monitoring is no longer needed. section 3.4
- Call select with an optional timeout. It blocks until events occur, then returns ready sockets and their triggering events. section 3.4
With non-blocking sockets, this loop can accept connections and process client data without blocking the thread or repeatedly consuming CPU. An infinite select loop reproduces the core machinery of an asyncio event loop. section 3.7
Listing 3.7 Using selectors to build a non-blocking server
import selectors
import socket
from selectors import SelectorKey
from typing import List, Tuple
selector = selectors.DefaultSelector()
server_socket = socket.socket()
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server_address = ('127.0.0.1', 8000)
server_socket.setblocking(False)
server_socket.bind(server_address)
server_socket.listen()
selector.register(server_socket, selectors.EVENT_READ)
while True:
events: List[Tuple[SelectorKey, int]] = selector.select(timeout=1) #1
if len(events) == 0: #2
print('No events, waiting a bit more!')
for event, _ in events:
event_socket = event.fileobj #3
if event_socket == server_socket: #4
connection, address = server_socket.accept()
connection.setblocking(False)
print(f"I got a connection from {address}")
selector.register(connection, selectors.EVENT_READ) #5
else:
data = event_socket.recv(1024) #6
print(f"I got some data: {data}")
event_socket.send(data)
A complete non-blocking echo-server loop registers the server socket, waits for readiness, accepts clients, and processes socket events. section 3.4
