pythonsocketsasyncore

asyncore.dispatcher in python: when are the handle_closed and handle_read executed?


There are two files: server.py and client.py, both written with the help of asyncore.dispatcher

Server.py

import asyncore, socket

class Server(asyncore.dispatcher):
    def __init__(self, host, port):
        asyncore.dispatcher.__init__(self)
        self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
        self.bind(('', port))
        self.listen(1)
        print "Waiting for connection..."

    def handle_accept(self):
        socket, address = self.accept()
        print 'Connection by', address
        socket.send("Hello Server")

    def handle_read(self):
        print "Reading..."
        out_buffer = self.recv(1024)
        if not out_buffer:
            self.close()
        print out_buffer

    def handle_closed(self):
        print "Server: Connection Closed"
        self.close()

s = Server('0.0.0.0', 5007)
asyncore.loop()

Client.py

import asyncore, socket

class Client(asyncore.dispatcher):
    def __init__(self, host, port):
        asyncore.dispatcher.__init__(self)
        self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
        self.connect((host, port))
        print "Client Start..."

    def handle_close(self):
        print "Client: Connection Closed"
        self.close()

    def handle_read(self):
        data = self.recv(1024)
        if data:
            print "Received ", data
        self.send("Hello Client")

c = Client('127.0.0.1', 5007)
asyncore.loop()

Result:

Execute server.py:

Waiting for connection...

then client.py:

Client Start...
Received  Hello Server
Client: Connection Closed
Client: Connection Closed

Finally the client.py exited, and there is one more line displayed in the ouput window of server.py and the server keeps running:

Connection by ('127.0.0.1', 58197)

There are something that I cannot understand:

  1. Why is the function handle_closed in client.py executed twice?

  2. Why isn't the function handle_reading in server.py executed? The client.py has sent message("Hello Client"), but why cannot the server receive it?

  3. Why isn't the function handle_closed in server.py executed? I want to execute some codes in server.py when the client exits, but it seems that it does nothing to do handle_closed in server.py?


Solution

  • Asyncore talk

    The handle_read() in server.py will never be called.

    But why?! It's a server class...

    Yes, but Server class uses its socket for listening any non-established connections. Any reads on it go to handle_accept(), where actual channel sockets (connected to some endpoint) should be given to new instance of some dispatcher-inherited class (preferably). In your Server's handle_accept() method sockets got by accept() were local and thus deleted upon exiting this function, so: new connection was accepted, text was sent and after that socket was immediately killed.

    Have some read on asyncore module and my answer in other question.

    Server

    You need to, like I said, make new class for connections in server.py:

    class ClientHandler(asyncore.dispatcher):
        def handle_read(self):
            data = self.recv(1024)
            if not data:
                return
            print "Received:", data
    
        def handle_close(self):
            print "Server: Connection Closed"
            self.close()
    

    Note here that reading don't need to manually close socket when null is recieved - asyncore takes care of properly closing connection.

    Then you have to instantiate it in Server when connection is made:

        def handle_accept(self):
            ...
            ClientHandler(socket)
    

    You also made spelling mistake in Server - method's proper name is handle_close. Though it wouldn't be useful. Everything client-connection related is in ClientHandler.

    Client

    In client.py you just need to modify handle_read():

        if data:
            print "Received ", data
    

    Change to:

        if not data:
            return
        print "Received ", data
    

    Why? Without this send() would be called even when socket is actually closed, resulting in handle_close() being called for second time, by asyncore. Like I said - asyncore takes care of this.

    Notes

    Now you can write more complex server-side class for connections. You could also learn how OS-level sockets work, so you won't get into trouble.

    asyncore itself is a pretty nice wrapper for sockets, but if you want to do higher-lever things, like HTTP or SMTP processing in some event-driven environment, then Twisted library would interest you!