The select module provides access to platform-specific I/O monitoring functions. The most portable interface is the POSIX function select() , which is available on Unix and Windows. The module also includes poll() , a Unix-only API, and several options that only work with specific variants of Unix.

The new selectors module provides a higher-level interface built on top of the APIs in select . It is easier to build portable code using selectors , so use that module unless the low-level APIs provided by select are somehow required.

Python’s select() function is a direct interface to the underlying operating system implementation. It monitors sockets, open files, and pipes (anything with a fileno() method that returns a valid file descriptor) until they become readable or writable or a communication error occurs. select() makes it easier to monitor multiple connections at the same time, and is more efficient than writing a polling loop in Python using socket timeouts, because the monitoring happens in the operating system network layer, instead of the interpreter.

Note Using Python’s file objects with select() works for Unix, but is not supported under Windows.

The echo server example from the socket section can be extended to watch for more than one connection at a time by using select() . The new version starts out by creating a non-blocking TCP/IP socket and configuring it to listen on an address.

select_echo_server.py ¶ import select import socket import sys import queue # Create a TCP/IP socket server = socket . socket ( socket . AF_INET , socket . SOCK_STREAM ) server . setblocking ( 0 ) # Bind the socket to the port server_address = ( 'localhost' , 10000 ) print ( 'starting up on {} port {} ' . format ( * server_address ), file = sys . stderr ) server . bind ( server_address ) # Listen for incoming connections server . listen ( 5 )

The arguments to select() are three lists containing communication channels to monitor. The first is a list of the objects to be checked for incoming data to be read, the second contains objects that will receive outgoing data when there is room in their buffer, and the third those that may have an error (usually a combination of the input and output channel objects). The next step in the server is to set up the lists containing input sources and output destinations to be passed to select() .

# Sockets from which we expect to read inputs = [ server ] # Sockets to which we expect to write outputs = []

Connections are added to and removed from these lists by the server main loop. Since this version of the server is going to wait for a socket to become writable before sending any data (instead of immediately sending the reply), each output connection needs a queue to act as a buffer for the data to be sent through it.

# Outgoing message queues (socket:Queue) message_queues = {}

The main portion of the server program loops, calling select() to block and wait for network activity.

while inputs : # Wait for at least one of the sockets to be # ready for processing print ( 'waiting for the next event' , file = sys . stderr ) readable , writable , exceptional = select . select ( inputs , outputs , inputs )

select() returns three new lists, containing subsets of the contents of the lists passed in. All of the sockets in the readable list have incoming data buffered and available to be read. All of the sockets in the writable list have free space in their buffer and can be written to. The sockets returned in exceptional have had an error (the actual definition of “exceptional condition” depends on the platform).

The “readable” sockets represent three possible cases. If the socket is the main “server” socket, the one being used to listen for connections, then the “readable” condition means it is ready to accept another incoming connection. In addition to adding the new connection to the list of inputs to monitor, this section sets the client socket to not block.

# Handle inputs for s in readable : if s is server : # A "readable" socket is ready to accept a connection connection , client_address = s . accept () print ( ' connection from' , client_address , file = sys . stderr ) connection . setblocking ( 0 ) inputs . append ( connection ) # Give the connection a queue for data # we want to send message_queues [ connection ] = queue . Queue ()

The next case is an established connection with a client that has sent data. The data is read with recv() , then placed on the queue so it can be sent through the socket and back to the client.

else : data = s . recv ( 1024 ) if data : # A readable client socket has data print ( ' received {!r} from {} ' . format ( data , s . getpeername ()), file = sys . stderr , ) message_queues [ s ] . put ( data ) # Add output channel for response if s not in outputs : outputs . append ( s )

A readable socket without data available is from a client that has disconnected, and the stream is ready to be closed.

else : # Interpret empty result as closed connection print ( ' closing' , client_address , file = sys . stderr ) # Stop listening for input on the connection if s in outputs : outputs . remove ( s ) inputs . remove ( s ) s . close () # Remove message queue del message_queues [ s ]

There are fewer cases for the writable connections. If there is data in the queue for a connection, the next message is sent. Otherwise, the connection is removed from the list of output connections so that the next time through the loop select() does not indicate that the socket is ready to send data.

# Handle outputs for s in writable : try : next_msg = message_queues [ s ] . get_nowait () except queue . Empty : # No messages waiting so stop checking # for writability. print ( ' ' , s . getpeername (), 'queue empty' , file = sys . stderr ) outputs . remove ( s ) else : print ( ' sending {!r} to {} ' . format ( next_msg , s . getpeername ()), file = sys . stderr ) s . send ( next_msg )

Finally, if there is an error with a socket, it is closed.

# Handle "exceptional conditions" for s in exceptional : print ( 'exception condition on' , s . getpeername (), file = sys . stderr ) # Stop listening for input on the connection inputs . remove ( s ) if s in outputs : outputs . remove ( s ) s . close () # Remove message queue del message_queues [ s ]

The example client program uses two sockets to demonstrate how the server with select() manages multiple connections at the same time. The client starts by connecting each TCP/IP socket to the server.

select_echo_multiclient.py ¶ import socket import sys messages = [ 'This is the message. ' , 'It will be sent ' , 'in parts.' , ] server_address = ( 'localhost' , 10000 ) # Create a TCP/IP socket socks = [ socket . socket ( socket . AF_INET , socket . SOCK_STREAM ), socket . socket ( socket . AF_INET , socket . SOCK_STREAM ), ] # Connect the socket to the port where the server is listening print ( 'connecting to {} port {} ' . format ( * server_address ), file = sys . stderr ) for s in socks : s . connect ( server_address )

Then it sends one piece of the message at a time via each socket and reads all responses available after writing new data.

for message in messages : outgoing_data = message . encode () # Send messages on both sockets for s in socks : print ( ' {} : sending {!r} ' . format ( s . getsockname (), outgoing_data ), file = sys . stderr ) s . send ( outgoing_data ) # Read responses on both sockets for s in socks : data = s . recv ( 1024 ) print ( ' {} : received {!r} ' . format ( s . getsockname (), data ), file = sys . stderr ) if not data : print ( 'closing socket' , s . getsockname (), file = sys . stderr ) s . close ()

Run the server in one window and the client in another. The output will look like this, with different port numbers.

$ python3 select_echo_server.py starting up on localhost port 10000 waiting for the next event connection from ('127.0.0.1', 61003) waiting for the next event connection from ('127.0.0.1', 61004) waiting for the next event received b'This is the message. ' from ('127.0.0.1', 61003) received b'This is the message. ' from ('127.0.0.1', 61004) waiting for the next event sending b'This is the message. ' to ('127.0.0.1', 61003) sending b'This is the message. ' to ('127.0.0.1', 61004) waiting for the next event ('127.0.0.1', 61003) queue empty ('127.0.0.1', 61004) queue empty waiting for the next event received b'It will be sent ' from ('127.0.0.1', 61003) received b'It will be sent ' from ('127.0.0.1', 61004) waiting for the next event sending b'It will be sent ' to ('127.0.0.1', 61003) sending b'It will be sent ' to ('127.0.0.1', 61004) waiting for the next event ('127.0.0.1', 61003) queue empty ('127.0.0.1', 61004) queue empty waiting for the next event received b'in parts.' from ('127.0.0.1', 61003) waiting for the next event received b'in parts.' from ('127.0.0.1', 61004) sending b'in parts.' to ('127.0.0.1', 61003) waiting for the next event ('127.0.0.1', 61003) queue empty sending b'in parts.' to ('127.0.0.1', 61004) waiting for the next event ('127.0.0.1', 61004) queue empty waiting for the next event closing ('127.0.0.1', 61004) closing ('127.0.0.1', 61004) waiting for the next event

The client output shows the data being sent and received using both sockets.