4

Python 2.6 Windows 7

協調的なマルチタスク プログラムの書き方について、できるだけ簡単なチュートリアルをまとめようとしています。サンプル アプリケーションとして、Python の非同期コア バックエンドを使用してチャット サーバーを作成しました。これはコミュニティにとって貴重なリソースになると思います。ただし、まだ機能していないため、この投稿を行います。

構造は次のとおりです。ChatServer のインスタンスは、リモート コンピューターで実行されます。ソケットは REMOTE_PORT をリッスンします。着信接続を検出すると、ChatHandler のインスタンスを生成して、その接続との通信を仲介します。さて、そのつながりは誰ですか?ユーザーのローカル マシンで、ChatDaemon のインスタンスを実行します。この男は LOCAL_PORT でリッスンします。このように彼に接続すると

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

彼は接続を検出し、LocalListener と Connection の 2 つを生成します。Connection はサーバーに接続し、上記の質問に答えます。LocalListener は、ユーザーからデータが来るのを待つだけです。データを送る場合

s.send("Hello, world!")

LocalListener がそれをピックアップして Connection に渡し、Connection がそれを ChatServer に送信します。次に、サーバーはデータを各 ChatHandler のバッファーに入れ、接続されているすべてのクライアントに送信します。Connection がそのデータを受け取ると、それを Daemon に渡します。Daemon はそれを画面に出力します。

(デーモン層は非常に複雑に見えますが、それがなければ、ユーザーがデータを送信するためのレイテンシーを低く保ちながら、asyncore の select() ループでのホット ループを防ぐために、他の複雑なことを行う必要があります。私はその道をたどりたくありません。)

問題は、デーモンへの接続が確立されていないように見えることです。私の正確な手順は

1 つの 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))
4

1 に答える 1

0

問題は、ChatDaemon で読み取り可能および書き込み可能の「return」キーワードを忘れたことです。

于 2013-01-25T08:24:42.023 に答える