An eventlet-based chat server

The eventlet library provides a high-level API for event-driven programming, but it does so in a style that mimics the procedural, blocking-IO style that we used in our multithreaded servers. The upshot is that we can effectively take our multithreaded chat server code, make a few minor modifications to it to use eventlet instead, and immediately gain the benefits of the event-driven model!

The eventlet library is available in PyPi, and it can be installed with pip, as shown here:

$ pip install eventlet
Downloading/unpacking eventlet

Note

The eventlet library automatically falls back to select if poll is not available, so it will run properly on Windows.

Once it's installed, create a new file called 4.1-chat_server-eventlet.py and save the following code in it:

import eventlet
import eventlet.queue as queue
import tincanchat

HOST = tincanchat.HOST
PORT = tincanchat.PORT
send_queues = {}

def handle_client_recv(sock, addr):
    """ Receive messages from client and broadcast them to
        other clients until client disconnects """
    rest = bytes()
    while True:
        try:
            (msgs, rest) = tincanchat.recv_msgs(sock)
        except (EOFError, ConnectionError):
            handle_disconnect(sock, addr)
            break
        for msg in msgs:
            msg = '{}: {}'.format(addr, msg)
            print(msg)
            broadcast_msg(msg)

def handle_client_send(sock, q, addr):
    """ Monitor queue for new messages, send them to client as
        they arrive """
    while True:
        msg = q.get()
        if msg == None: break
        try:
            tincanchat.send_msg(sock, msg)
        except (ConnectionError, BrokenPipe):
            handle_disconnect(sock, addr)
            break

def broadcast_msg(msg):
    """ Add message to each connected client's send queue """
    for q in send_queues.values():
        q.put(msg)

def handle_disconnect(sock, addr):
    """ Ensure queue is cleaned up and socket closed when a client
        disconnects """
    fd = sock.fileno()
    # Get send queue for this client
    q = send_queues.get(fd, None)
    # If we find a queue then this disconnect has not yet
    # been handled
    if q:
        q.put(None)
        del send_queues[fd]
        addr = sock.getpeername()
        print('Client {} disconnected'.format(addr))
        sock.close()

if __name__ == '__main__':
    server = eventlet.listen((HOST, PORT))
    addr = server.getsockname()
    print('Listening on {}'.format(addr))

    while True:
        client_sock,addr = server.accept()
        q = queue.Queue()
        send_queues[client_sock.fileno()] = q
        eventlet.spawn_n(handle_client_recv,
                         client_sock,
                         addr)
        eventlet.spawn_n(handle_client_send,
                         client_sock,
                         q,
                         addr)
        print('Connection from {}'.format(addr))

We can test this with our multithreaded client to ensure that it works as expected.

As you can see, it's pretty much identical to our multithreaded server, with a few changes made so as to use eventlet. Notice that we've removed the synchronization code and the lock around send_queues. We're still using queues, although they're the eventlet library's queues, because we want to retain the blocking behavior of Queue.get().

Note

There are more examples of using eventlet for programming on the eventlet site at http://eventlet.net/doc/examples.html.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.141.30.210