Open selfboot opened 7 years ago
poll is not supported by all operating systems, OS X does not support poll.
Instead of using poll, use select.kqueue() on OSX. It's similar to 'epoll' on Linux in that you can more efficiently register for types of file-descriptor / file system events which can be used in asynchronous code. Much more efficient than polling.
The poll() function provides similar features to #11 select(), but the underlying implementation is more efficient.
Poll echo server
An echo server built on
poll()
starts with the same socket configuration code used in the example #11 . Python implements poll() with a class that manages the registered data channels being monitored. Channels are added by callingregister()
with flags indicating which events are interesting for that channel. The full set of flags is:The echo server will be setting up some sockets just for reading, and others to be read from or written to. The appropriate combinations of flags are saved to the local variables READ_ONLY and READ_WRITE.
Since poll() returns a list of tuples containing the file descriptor for the socket and the event flag, a mapping from file descriptor numbers to objects is needed to retrieve the socket to read or write from it.
Then:
unregister()
is used to tell the poll object to ignore the socket.POLLHUP
flag indicates a client that “hung up” the connection without closing it cleanly. The server stops polling clients that disappear.POLLERR
cause the server to close the socket.poll_server.py is here. (click to unfold)
```python #! /usr/bin/env python # -*- coding: utf-8 -*- # py 2.X from __future__ import print_function import socket import select 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 %s port %s' % server_address) server.bind(server_address) # Listen for incoming connections server.listen(5) # Keep up with the queues of outgoing messages message_queues = {} # Do not block forever(milliseconds) TIMEOUT = 1000 # Commonly used flag setes READ_ONLY = select.POLLIN | select.POLLPRI | select.POLLHUP | select.POLLERR READ_WRITE = READ_ONLY | select.POLLOUT # Set up the poller poller = select.poll() poller.register(server, READ_ONLY) # Map file description to socket objects fd_to_sockets = {server.fileno(): server} while True: # Wait for at least one of the sockets to be ready for processing print('\nWaiting for the next event') events = poller.poll(TIMEOUT) for fd, flag in events: # Retrieve the actual socket from its file descriptor s = fd_to_sockets[fd] # Handle inputs if flag & (select.POLLIN | select.POLLPRI): # A readable server is ready to accept a connection if s is server: connection, client_address = s.accept() print('new connection from', client_address) connection.setblocking(0) fd_to_sockets[connection.fileno()] = connection poller.register(connection, READ_ONLY) # Give the connection a queue for the data we want to send message_queues[connection] = Queue.Queue() else: data = s.recv(1024) # A readable client socket has data if data: print('received "%s" from %s' % (data, s.getpeername())) # Add output channel for response message_queues[s].put(data) poller.modify(s, READ_WRITE) else: # Interpret empty result as closed connection print('closing ', client_address, 'after reading no data') # Stop listening for input on the connection poller.unregister(s) s.close() # Remove the message queue del message_queues[s] elif flag & select.POLLHUP: # Client hung up print('closing', client_address, ' after receiving HUP') # Stop listening for input on the connection poller.unregister(s) s.close() elif flag & select.POLLOUT: # Socket is ready to send data, if there is any to send try: next_msg = message_queues[s].get_nowait() except Queue.Empty: # No message waiting so stop checking for writability print('output queue for', s.getpeername(), 'is empty') poller.modify(s, READ_ONLY) else: print('sending "%s" to "%s"' % (next_msg, s.getpeername())) s.send(next_msg) elif flag & select.POLLERR: print('handling exceptional condition for', s.getpeername()) poller.unregister(s) s.close() # Remove message queue del message_queues[s] ```Client is just like the #11 client.py.
Run Result
Run the server.py. (click to unfold)
```bash $ python server.py Waiting for the next event Waiting for the next event new connection from ('127.0.0.1', 45096) Waiting for the next event new connection from ('127.0.0.1', 45098) Waiting for the next event received "This is the message." from ('127.0.0.1', 45096) received "This is the message." from ('127.0.0.1', 45098) Waiting for the next event sending "This is the message." to "('127.0.0.1', 45096)" sending "This is the message." to "('127.0.0.1', 45098)" Waiting for the next event output queue for ('127.0.0.1', 45096) is empty output queue for ('127.0.0.1', 45098) is empty Waiting for the next event received "It will be sent" from ('127.0.0.1', 45096) received "It will be sent" from ('127.0.0.1', 45098) Waiting for the next event sending "It will be sent" to "('127.0.0.1', 45096)" sending "It will be sent" to "('127.0.0.1', 45098)" Waiting for the next event output queue for ('127.0.0.1', 45096) is empty output queue for ('127.0.0.1', 45098) is empty Waiting for the next event received "in parts" from ('127.0.0.1', 45096) received "in parts" from ('127.0.0.1', 45098) Waiting for the next event sending "in parts" to "('127.0.0.1', 45096)" sending "in parts" to "('127.0.0.1', 45098)" Waiting for the next event output queue for ('127.0.0.1', 45096) is empty output queue for ('127.0.0.1', 45098) is empty Waiting for the next event closing ('127.0.0.1', 45098) after reading no data closing ('127.0.0.1', 45098) after reading no data Waiting for the next event Waiting for the next event ```Run the client.py. (click to unfold)
```bash $ python client.py connecting to localhost port 10000 ('127.0.0.1', 45096): sending "This is the message." ('127.0.0.1', 45098): sending "This is the message." ('127.0.0.1', 45096): received "This is the message." ('127.0.0.1', 45098): received "This is the message." ('127.0.0.1', 45096): sending "It will be sent" ('127.0.0.1', 45098): sending "It will be sent" ('127.0.0.1', 45096): received "It will be sent" ('127.0.0.1', 45098): received "It will be sent" ('127.0.0.1', 45096): sending "in parts" ('127.0.0.1', 45098): sending "in parts" ('127.0.0.1', 45096): received "in parts" ('127.0.0.1', 45098): received "in parts" ```Ref
select – Wait for I/O Efficiently--poll