Handling multiple I/O operations concurrently in Python often leads developers to threading or asyncio. But for lower-level, high-performance needs — like building lightweight network servers or event-driven applications — Python’s selectors
module offers a simple yet powerful tool for I/O multiplexing. Let’s dive into how it works and how you can apply it in real-world scenarios.
🔧 What Is I/O Multiplexing?
I/O multiplexing is a method that lets you monitor multiple I/O streams (sockets, files, pipes) simultaneously, reacting only when one is ready. This is crucial for writing scalable network services without spawning threads for every connection.
📚 Enter the selectors
Module
The selectors
module is a high-level wrapper around select
, poll
, epoll
, or kqueue
, depending on what your OS supports. It abstracts these system calls into a unified API that’s easier to manage.
🛠 Example: Echo Server With `selectors`
Let’s build a simple echo server using the selectors
module.
import selectors
import socket
sel = selectors.DefaultSelector()
def accept(sock):
conn, addr = sock.accept()
print('Accepted connection from', addr)
conn.setblocking(False)
sel.register(conn, selectors.EVENT_READ, read)
def read(conn):
data = conn.recv(1024)
if data:
print('Echoing:', repr(data))
conn.sendall(data)
else:
print('Closing connection')
sel.unregister(conn)
conn.close()
host, port = 'localhost', 65432
lsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
lsock.bind((host, port))
lsock.listen()
print(f'Server listening on {(host, port)}')
lsock.setblocking(False)
sel.register(lsock, selectors.EVENT_READ, accept)
try:
while True:
events = sel.select(timeout=None)
for key, _ in events:
callback = key.data
callback(key.fileobj)
except KeyboardInterrupt:
print('Server shutting down')
finally:
sel.close()
🧠 What's Going On?
- Selector registration: We register the listening socket and client sockets for read events.
-
Event loop: We use
sel.select()
to wait for any registered socket to become ready. - Callbacks: Each socket has a handler (accept or read) attached as callback data.
🚀 Advantages
- Works across all major OSes
- No threading overhead
- Perfect for low-level networking and custom protocols
🎯 Use Cases
- Custom TCP servers
- Proxies
- Low-level HTTP/WebSocket handling
🧩 Conclusion
Python’s selectors
module is an underused gem for event-driven, concurrent I/O. It’s a great choice when you need control and performance without the complexity of asyncio or threading. Try it out in your next network project — especially when scaling efficiently matters!
If this post helped you, consider supporting me: buymeacoffee.com/hexshift