构建异步套接字教程:如何在python中连接到侦听套接字?

时间:2013-01-15 06:18:01

标签: python asynchronous asyncore

python 2.6 Windows 7

我正在尝试整理一个尽可能简单的如何编写协作式多任务程序的教程。作为一个示例应用程序,我用python的asyncore后端编写了一个聊天服务器。我认为这将是社区的宝贵资源。但是,我还没有开始工作,因此这篇文章。

结构如下。 ChatServer的实例在远程计算机上运行。它的套接字侦听REMOTE_PORT。当它检测到传入连接时,它会生成一个ChatHandler实例来调解与该连接的通信。现在,谁是那个联系?在用户的本地计算机上,我们运行ChatDaemon的实例。这家伙听LOCAL_PORT。当你像这样连接他时

import socket
s = socket.socket()
s.connect(('localhost',LOCAL_PORT))

他检测到连接并产生两件事,一个是LocalListener和一个Connection。 Connection连接到服务器,回答上面的问题。 LocalListener只是等待来自用户的数据。如果您发送数据

s.send("Hello, world!")

LocalListener将其选中,并将其提供给Connection,然后Connection将其发送到ChatServer。然后,服务器将数据放入每个ChatHandler的缓冲区中,以发送给所有连接的客户端。当Connection接收到该数据时,它会将其传递给守护进程,守护进程将其打印到屏幕上。

(守护进程层似乎过于复杂但没有它你必须做其他复杂的事情来防止asyncore的select()循环中的热循环,同时保持用户发送数据的延迟低。我不想走那条路。)

问题是似乎没有与守护进程的连接。我的确切步骤是

在一个python会话中

d = ChatDaemon('localhost')
d.start()

当我这样做时,我看到消息“聊天守护程序绑定到'localhost:7668'按预期方式。

在另一个python会话中

import socket
s = socket.socket()
s.connect(('localhost',7668))

当我这样做时,我没有看到“有新的本地连接”行。

我编辑了etc / hosts文件,将'localhost'映射到127.0.0.1,然后安装了Microsoft Loopback适配器。

编辑:我找到并修复了问题。现在,使用asyncore作为一个非常简单的聊天实现,现在可以接受下面的代码。

这是来源

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) > 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) > 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))

1 个答案:

答案 0 :(得分:0)

问题是在ChatDaemon中我忘记了可读写的“return”关键字