Twisted - 从服务器向客户端发送数据

4 投票
1 回答
3489 浏览
提问于 2025-04-16 22:05

我刚接触Twisted,读了很多和我遇到的问题类似的帖子。不过,我还是没办法从之前的回答中找到解决我简单问题的方法。我也看了Twisted的常见问题解答部分,但还是搞不明白。

我的问题是,我有一个服务器在某个端口上监听,当我收到一个“START”命令时,我想和几个客户端进行交流。举个例子,我用一个客户端,它给我提供了一个幸运饼干。但是,我在服务器代码中无法和这个客户端进行交流。你能告诉我我哪里出错了吗?以下是我的代码:

from twisted.internet import reactor, protocol
from twisted.internet.protocol import Protocol, Factory

class FakeTelnet(Protocol):
    def connectionMade(self):
        print 'local connection made'
        self.otherFact = protocol.ClientFactory()
        self.otherFact.protocol = EchoClient
        self.factory.clients.append(self.otherFact.protocol)
        reactor.connectTCP('psrfb6',10999, self.otherFact)

    def dataReceived(self, data):

        if 'START' in data:
            # send a command to cookie server.
            for client in self.factory.clients:
                client.transport.write('START\r\n')

    def connectionLost(self):
        print 'connection lost'

class EchoClient(Protocol):
    """Once connected, send a message, then print the result."""

    def connectionMade(self):
        print "Connection to cookie server"

    def dataReceived(self, data):
        "As soon as any data is received, write it back."
        print "Fortune Server said:", data

    def connectionLost(self):
        print "connection lost"

    def send_stuff(data):
        self.transport.write(data);

class MyFactory(Factory):
    protocol = FakeTelnet
    def __init__(self, EchoClient):
        self.clients = []
        self.otherCli = EchoClient

reactor.listenTCP(5823, MyFactory(EchoClient))
reactor.run()

1 个回答

5
class FakeTelnet(Protocol):
    def connectionMade(self):
        print 'local connection made'
        self.otherFact = protocol.ClientFactory()
        self.otherFact.protocol = EchoClient

在这里,你把 self.otherFact.protocol 设置为 EchoClient 这个

        self.factory.clients.append(self.otherFact.protocol)

这里你把 EchoClient 这个 加入到 self.factory.clients 的列表里。这会导致 self.factory.clients 里反复出现 EchoClient 这个

def dataReceived(self, data):
    if 'START' in data:
        # send a command to cookie server.
        for client in self.factory.clients:
            client.transport.write('START\r\n')

在这里你试着写入 EchoClient.transport,但通常它会是 None,因为只有协议类的实例才能连接到传输层,而不是类本身。

你可以试着把连接的实例添加到 self.factory.clients 里。我觉得你已经成功创建了这样的实例,因为在 FakeTelnet.connectionMade 里有这一行代码:

    reactor.connectTCP('psrfb6',10999, self.otherFact)

不过,你并没有做任何事情来把连接的协议放入 self.factory.clients 列表里。也许你可以这样定义 self.otherFact

class OtherFactory(ClientFactory):
    protocol = EchoClient

    def __init__(self, originalFactory):
        self.originalFactory = originalFactory

    def buildProtocol(self, addr):
        proto = ClientFactory.buildProtocol(self, addr)
        self.originalFactory.clients.append(proto)
        return proto

你还需要在某个时候从 clients 列表中 移除 一些东西。也许可以在协议的 connectionLost 回调中进行:

class EchoClient(Protocol):
    ...
    def connectionLost(self, reason):
        self.factory.originalFactory.clients.remove(self)

最后,如果你处理的是基于行的数据,可能想用 twisted.protocols.basic.LineOnlyReceiver 作为 FakeTelnet 的基类,并把你的逻辑放在它的 lineReceived 回调中。因为 dataReceived 回调并不保证数据的边界,所以你可能永远看不到一个包含 "START" 的完整字符串。例如,你可能会收到两次 dataReceived 的调用,一次是 "ST",另一次是 "ART"

撰写回答