📘 Premium Read: Access my best content on Medium member-only articles — deep dives into Java, Spring Boot, Microservices, backend architecture, interview preparation, career advice, and industry-standard best practices.
✅ Some premium posts are free to read — no account needed. Follow me on Medium to stay updated and support my writing.
🎓 Top 10 Udemy Courses (Huge Discount): Explore My Udemy Courses — Learn through real-time, project-based development.
▶️ Subscribe to My YouTube Channel (172K+ subscribers): Java Guides on YouTube
The select
module in Python provides tools to efficiently manage multiple I/O operations. It allows you to monitor multiple file descriptors to see if they have any I/O events (like readability or writability) ready. This is particularly useful in network programming where you need to handle multiple connections simultaneously.
Table of Contents
- Introduction
- Key Functions
select.select
select.poll
select.epoll
select.kqueue
- Examples
- Using
select
for I/O Multiplexing - Using
poll
for I/O Multiplexing - Using
epoll
for I/O Multiplexing - Using
kqueue
for I/O Multiplexing
- Using
- Real-World Use Case
- Conclusion
- References
Introduction
The select
module provides mechanisms to efficiently manage multiple I/O streams, such as sockets or files. It helps in determining which file descriptors are ready for reading, writing, or have encountered an exceptional condition. This is crucial for implementing high-performance network servers and clients.
Key Functions
select.select
Monitors multiple file descriptors to see if they are ready for some kind of I/O operation.
import select
readable, writable, exceptional = select.select(inputs, outputs, errors, timeout)
select.poll
Provides a more scalable way to monitor file descriptors compared to select
.
import select
poller = select.poll()
poller.register(fd, select.POLLIN | select.POLLOUT)
events = poller.poll(timeout)
select.epoll
Provides an interface to the Linux epoll
system call, which is more efficient for a large number of file descriptors.
import select
epoll = select.epoll()
epoll.register(fd, select.EPOLLIN | select.EPOLLOUT)
events = epoll.poll(timeout)
select.kqueue
Provides an interface to the BSD kqueue
system call, which is efficient for managing many file descriptors.
import select
kq = select.kqueue()
kevent = select.kevent(fd, filter=select.KQ_FILTER_READ, flags=select.KQ_EV_ADD)
kq.control([kevent], 0)
events = kq.control(None, 1, timeout)
Examples
Using select for I/O Multiplexing
import socket
import select
# Create two non-blocking sockets
s1 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s1.setblocking(0)
s1.bind(('localhost', 9001))
s1.listen(5)
s2 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s2.setblocking(0)
s2.bind(('localhost', 9002))
s2.listen(5)
inputs = [s1, s2]
outputs = []
errors = []
while inputs:
readable, writable, exceptional = select.select(inputs, outputs, errors)
for s in readable:
if s is s1:
conn, addr = s.accept()
print(f'Connection from {addr} on socket 1')
elif s is s2:
conn, addr = s.accept()
print(f'Connection from {addr} on socket 2')
for s in exceptional:
inputs.remove(s)
s.close()
Using poll for I/O Multiplexing
import socket
import select
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setblocking(0)
server.bind(('localhost', 9000))
server.listen(5)
poller = select.poll()
poller.register(server, select.POLLIN)
connections = {}
while True:
events = poller.poll()
for fd, flag in events:
if fd == server.fileno():
conn, addr = server.accept()
conn.setblocking(0)
poller.register(conn, select.POLLIN)
connections[conn.fileno()] = conn
elif flag & select.POLLIN:
conn = connections[fd]
data = conn.recv(1024)
if data:
print(f'Received data: {data}')
else:
poller.unregister(fd)
conn.close()
del connections[fd]
Using epoll for I/O Multiplexing
import socket
import select
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setblocking(0)
server.bind(('localhost', 9000))
server.listen(5)
epoll = select.epoll()
epoll.register(server.fileno(), select.EPOLLIN)
connections = {}
while True:
events = epoll.poll()
for fd, event in events:
if fd == server.fileno():
conn, addr = server.accept()
conn.setblocking(0)
epoll.register(conn.fileno(), select.EPOLLIN)
connections[conn.fileno()] = conn
elif event & select.EPOLLIN:
conn = connections[fd]
data = conn.recv(1024)
if data:
print(f'Received data: {data}')
else:
epoll.unregister(fd)
conn.close()
del connections[fd]
Using kqueue for I/O Multiplexing
import socket
import select
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setblocking(0)
server.bind(('localhost', 9000))
server.listen(5)
kq = select.kqueue()
kevent = select.kevent(server.fileno(), filter=select.KQ_FILTER_READ, flags=select.KQ_EV_ADD)
kq.control([kevent], 0)
connections = {}
while True:
events = kq.control(None, 1)
for event in events:
if event.ident == server.fileno():
conn, addr = server.accept()
conn.setblocking(0)
kevent = select.kevent(conn.fileno(), filter=select.KQ_FILTER_READ, flags=select.KQ_EV_ADD)
kq.control([kevent], 0)
connections[conn.fileno()] = conn
elif event.filter == select.KQ_FILTER_READ:
conn = connections[event.ident]
data = conn.recv(1024)
if data:
print(f'Received data: {data}')
else:
kevent = select.kevent(conn.fileno(), filter=select.KQ_FILTER_READ, flags=select.KQ_EV_DELETE)
kq.control([kevent], 0)
conn.close()
del connections[event.ident]
Real-World Use Case
Chat Server
import socket
import select
def chat_server():
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setblocking(0)
server.bind(('localhost', 9000))
server.listen(5)
inputs = [server]
outputs = []
message_queues = {}
while inputs:
readable, writable, exceptional = select.select(inputs, outputs, inputs)
for s in readable:
if s is server:
conn, addr = s.accept()
print(f'New connection from {addr}')
conn.setblocking(0)
inputs.append(conn)
message_queues[conn] = []
else:
data = s.recv(1024)
if data:
message_queues[s].append(data)
if s not in outputs:
outputs.append(s)
else:
if s in outputs:
outputs.remove(s)
inputs.remove(s)
s.close()
del message_queues[s]
for s in writable:
try:
next_msg = message_queues[s].pop(0)
except IndexError:
outputs.remove(s)
else:
s.send(next_msg)
for s in exceptional:
inputs.remove(s)
if s in outputs:
outputs.remove(s)
s.close()
del message_queues[s]
if __name__ == '__main__':
chat_server()
Conclusion
The select
module in Python is used for managing multiple I/O operations. It provides several mechanisms (select
, poll
, epoll
, kqueue
) to efficiently monitor file descriptors for I/O events, making it essential for building high-performance networked applications.
Comments
Post a Comment
Leave Comment