Note that there are some explanatory texts on larger screens.

plurals
  1. POBuilding asynchronous socket tutorial: how do I connect to listening socket in python?
    primarykey
    data
    text
    <p>python 2.6 Windows 7</p> <p>I am trying to put together an as simple as possible tutorial of how to write cooperative multitasking programs. As an example application I've written a chat server with python's asyncore backend. I think this will be a valuable resource for the community. However, I have not gotten it to work yet, hence this post.</p> <p>The structure is as follows. An instance of ChatServer runs on a remote computer. It's socket listens on REMOTE_PORT. When it detects an incoming connection, it spawns an instance of ChatHandler to mediate communication with that connection. Now, who is that connection? On the user's local machine we run an instance of ChatDaemon. This guy listens on LOCAL_PORT. When you connect to him like this</p> <pre><code>import socket s = socket.socket() s.connect(('localhost',LOCAL_PORT)) </code></pre> <p>he detects the connection and spawns two things, a LocalListener and a Connection. The Connection connects to the server, answering our question from above. The LocalListener simply waits for data coming in from the user. If you send data</p> <pre><code>s.send("Hello, world!") </code></pre> <p>the LocalListener picks it up, and gives it to the Connection, which then sends it to the ChatServer. The server then puts the data in each ChatHandler's buffer to be sent out to all connected clients. When the Connection receives that data, it passes it to the Daemon who prints it to the screen.</p> <p>(The Daemon layer seems overly complex but without it you have to do other complicated things to prevent hot loops in asyncore's select() loop whilst keeping latency for the user sending data low. I don't want to go down that road.)</p> <p><strong>The problem is that the connection to the Daemon does not seem to be made</strong>. My exact steps are</p> <p>In one python session</p> <pre><code>d = ChatDaemon('localhost') d.start() </code></pre> <p>When I do this I see the message "Chat daemon binding to 'localhost: 7668' as expected.</p> <p>In another python session</p> <pre><code>import socket s = socket.socket() s.connect(('localhost',7668)) </code></pre> <p>When I do this, I do not see the "Got new local connection" line printed.</p> <p>I have edited my etc/hosts file to map 'localhost' to 127.0.0.1, and I installed the Microsoft Loopback adapter.</p> <p><strong>EDIT:</strong> I have found and fixed the problem. The code below should now be acceptable as a very simple chat implementation using asyncore.</p> <p>Here is the source</p> <pre><code>import asyncore import socket import sys LOCAL_HOST = 'localhost' LOCAL_PORT = 7668 REMOTE_HOST = 'localhost' REMOTE_PORT = 7667 class LocalListener(asyncore.dispatcher): """Receive data from user, putting into cxn's buffer""" def __init__(self, sock, cxn): self.cxn = cxn asyncore.dispatcher.__init__(self, sock) def writable(self): return False def readable(self): return True def handle_read(self): data = self.recv(4096) if data: self.cxn.buf = self.cxn.buf + data class Connection(asyncore.dispatcher): """Mediates between user and server""" def __init__(self, host, port, master): asyncore.dispatcher.__init__(self) self.create_socket(socket.AF_INET, socket.SOCK_STREAM) self.connect((host,port)) self.buf="" def writable(self): return len(self.buf) &gt; 0 def readable(self): return True def handle_read(self): data = self.recv(4096) if data: self.master.newMessage(data) def handle_write(self): sent = self.send(self.buf) self.buf = self.buf[sent:] class ChatDaemon(asyncore.dispatcher): """Listen for local connections and dispatch in/out data""" ADDRESS_FAMILY = socket.AF_INET SOCKET_TYPE = socket.SOCK_STREAM def __init__(self, remoteHost, remotePort=REMOTE_PORT, localHost=LOCAL_HOST, localPort=LOCAL_PORT): self.remoteHost = remoteHost self.remotePort = remotePort self.localHost = localHost self.localPort = localPort self.buffer = "" asyncore.dispatcher.__init__(self) def writable(self): return False def readable(self): return True def newMessage(self, data): print data def start(self): """Listen for user connection on local port""" self.create_socket(self.ADDRESS_FAMILY, self.SOCKET_TYPE) print("Chat deamon binding to '%s': %s"%(self.localHost,self.localPort)) self.bind((self.localHost,self.localPort)) self.listen(1) asyncore.loop() def handle_accept(self): """Spawn local reader and remote reader/writer""" print "Got new local connection" (connSock, localAddress) = self.accept() print("New connection address is %s"%localAddress) #Make a server connection cxn = Connection(self.remoteHost, self.remotePort, self) #Connect to local user LocalListener(connSock, cxn) ### SERVER ### class ChatHandler(asyncore.dispatcher): def __init__(self, sock, map, server): self.server = server self.buffer = '' asyncore.dispatcher.__init__(self, sock, map) def writable(self): return len(self.buffer) &gt; 0 def readable(self): return True def handle_read(self): """Notify server of any new incoming data""" data = self.recv(4096) if data: self.server.newMessage(data, self) def handle_write(self): """send some amount of buffer""" sent = self.send(self.buffer) self.buffer = self.buffer[sent:] class ChatServer(asyncore.dispatcher): """Receive and forward chat messages When a new connection is made we spawn a dispatcher for that connection. """ ADDRESS_FAMILY = socket.AF_INET SOCKET_TYPE = socket.SOCK_STREAM def __init__(self, host=REMOTE_HOST, port=REMOTE_PORT): self.map = {} self.address = (host,port) self.clients = [] asyncore.dispatcher.__init__(self, map=self.map) def serve(self): """Bind to socket and start asynchronous loop""" self.create_socket(self.ADDRESS_FAMILY, self.SOCKET_TYPE) self.bind(self.address) self.listen(1) asyncore.loop(map=self.map) def writable(self): return False def readable(self): return True def newMessage(self, data, fromWho): """Put data in all clients' buffers""" for client in self.clients: client.buf = client.buf + data def handle_accept(self): """Deal with newly accepted connection""" print 'got new connection' (connSock, clientAddress) = self.accept() self.clients.append(ChatHandler(connSock, self.map, self)) </code></pre>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload