使用Twisted实现交互式Python客户端/服务器

4

我一直在努力理解如何让Twisted执行“交互式”客户端/服务器行为。

我成功地编写了一对Protocol和ClientFactory类,可以连接到服务,并执行立即的查询/响应(请参见:connectionMade -> self.queryStatus)。这符合预期,从Factory类打印服务器的响应。

现在我的问题是,我有些外部事件必须导致数据被发送出去,同时始终监听潜在的传入数据。但是一旦reactor.run()循环开始运行,我不知道我的应用程序的其余部分如何触发数据发送。

我尝试了几种不同的方法,但这是最简单的方法,它按照上述方式处理了接收部分:

class myListenerProtocol(LineReceiver):
    delimiter = '\n'

    def connectionMade(self):
        print("Connected to: %s" % self.transport.getPeer())
        self.queryStatus(1)

    def dataReceived(self, data):
        print("Receiving Data from %s" % self.transport.getPeer())
        ...
        self.commandReceived(self.myData)

    def commandReceived(self, myData):
        self.factory.commandReceived(myData)

    def connectionLost(self, reason):
        print("Disconnected.")

    def queryStatus(self, CommandValue):
        ...
        strSend = CommandValue # or some such
        self.transport.write(strSend)

class mySocketFactory(ClientFactory):
    protocol = myListenerProtocol

    def __init__(self):
        pass

    def buildProtocol(self, address):
        proto = ClientFactory.buildProtocol(self, address)
        return proto

    def commandReceived(self, myData):
        print myData
        reactor.stop() # It won't normally stop after recv

    def clientConnectionFailed(self, connector, reason):
        print("Connection failed.")
        reactor.stop()


def main():
    f = mySocketFactory()
    reactor.connectTCP("10.10.10.1", 1234, f)
    reactor.run()

我认为这很简单,但是数小时的不断尝试和查阅文档并没有让我很好地理解我应该如何处理这种情况。
2个回答

1
我的问题是,我需要在监听潜在传入数据的同时,处理必须发送数据的外部事件。但一旦reactor.run()循环开始,我不知道如何触发数据发送。 "外部事件"?例如什么?连接上到达的数据?很好,运行反应器意味着您实际上将能够处理该数据。或者可能有人在GUI中点击按钮?尝试使用其中一个GUI集成反应器 - 再次说明,在运行反应器之前,无法处理这些事件。您可能会因为认为主函数应该执行reactor.run()然后继续执行其他操作而卡住。这不是它的工作方式。当您编写事件驱动程序时,您定义所有事件源,然后让事件循环在这些源上到达事件时调用您的处理程序。

1
不,我的意思是我将把它作为更大应用程序的组件包含在内。举个例子,假设它将作为客户端组件运行,在您使用主GUI驱动的应用程序时连接到后台。因此,主(现有)应用程序中发生事件。触发器无关紧要,但它会导致此连接的客户端会话发送数据。同时,它将接受传入的连接,我必须处理这些连接。 - Dave
它的触发方式很重要,因为这将决定您如何集成不同的组件。如果您想完全忽略其他组件的实现方式,则可以在单独的进程中运行它们并使用某种IPC / RPC。虽然这通常需要更多的工作,但如果您不想指定有关每个组件将如何实现的任何信息,那么这是使两个组件协同工作的最直接的方法。 - Jean-Paul Calderone
如果不清楚的话,我认为你应该明确指定其他组件的工作方式,然后再想办法将其集成到Twisted事件循环中。以上评论是在你不这样做的情况下。 - Jean-Paul Calderone

-1

嗯,有很多方法可以做到这一点,最好的方法真的取决于您的应用程序的上下文,因此我在此不会详细介绍一种方法,而是链接您最近在黑客新闻上阅读的内容:

这是一个很好的使用案例示例,尽管它可能不适用于你正在处理的内容(或者你可能已经阅读过它):

顺便提一下,您也可以看看gevent或tornado,它们擅长处理这种事情。

如果您的其他“事件”来自GUI工具包(如GTK或QT),请非常小心GIL,即使您只想要命令行事件,您也需要线程,并且仍然要小心。

最后,如果您想要更多的交互,您可以为服务器编写不同类型的“对等体”,与您正在处理的不同用例进行交互(一个连接到GUI的客户端,另一个连接到CLI,另一个连接到数据库,另一个连接到SAAS' API等)。

换句话说,如果您的设计不起作用,请尝试改变您的视角!


你不必担心GIL。只要不使用线程即可。这篇文章中没有任何关于需要使用线程的暗示,因此GIL是无关紧要的。 - Glyph

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接