在 Python 的 Twisted 中处理收到消息的最佳方式是什么?

What is the best way of handling received messages in Twisted for Python?

我是 Python 的菜鸟,正在寻求架构方面的帮助。这是我的设置:我有一个用 LiveCode 编写的遗留客户端应用程序,它在多个位置运行以根据服务器的需求显示同步信息。把它想象成一个售货亭。这个客户端不会去任何地方。

服务器应用程序是我在 Python 中重写的内容。我的目标是让服务器应用程序 运行 不断侦听客户端套接字连接,并 sending/receiving 数据 to/from 这些客户端。我已经成功地在这个 LiveCode 客户端应用程序和使用 Twisted 进行套接字处理的 python 脚本之间传递了消息,所以现在我需要开始处理这些消息。代码看起来像这样:

from twisted.internet.protocol import Factory
from twisted.protocols.basic import LineReceiver
from twisted.internet import reactor

class MessageListener(LineReceiver):

    def __init__(self, users):
        self.users = users
        self.name = None

    def connectionMade(self):
        d = self.transport.getHost()
        print("Connection established with {}:{}".format(d.host, d.port))

    def connectionLost(self, reason):
        print("Connection lost: {}".format(reason))
        if self.name in self.users:
            del self.users[self.name]

    def dataReceived(self, line):
        d = self.transport.getHost()
        print("Received message from {}:{}...{}".format(d.host, d.port, line))
        self.handle_GOTDATA(line)

    def handle_GOTDATA(self, msg):
        #convert "msg" to string and parse it into the necessary chunks

        #*****Go do something based on the requestor and the command*****
        #Use if-elif or dictionary to determine which function to run
        #based on what the string tells us.
        #Should these functions be defined here or in a separate class?

class MessageListenerFactory(Factory):

    def __init__(self):
        self.users = {} # maps user names to Chat instances

    def buildProtocol(self, addr):
        return MessageListener(self.users)

reactor.listenTCP(50010, MessageListenerFactory())

reactor.run()

几个问题:

  1. handle_GOTDATA() 函数是我获取接收到的消息的地方,将其解析为告诉我如何处理数据的块,然后根据需要调用不同的函数关于需要用这些数据做什么。 我是只在同一个“MessageListener”class 中定义所有 20 个这些函数,还是写一个单独的 class 来保留所有这些函数?我可能同时收到 10 条消息,它们可能需要调用相同的函数,所以我不确定这里的最佳架构方法。

  2. 我还想构建一个 GUI 来与服务器交互,以便偶尔进行一些故障排除和监控。我熟悉 Tkinter,这很好,我可以在单独的文件中编写 GUI,并让它通过套接字连接到服务器。但是我会使用上面实现的相同套接字侦听器并向它传递类似的消息吗?或者我应该建立一个单独的 class 和工厂来监听 GUI 连接?

如果您要使用 LineReceiver,则不应覆盖 dataReceived。相反,覆盖 lineReceived。如果您的协议不是面向行的,您可能不想使用 LineReceiver。另外,在任何一种情况下,您都可能需要考虑使用 Tubes 界面。

Do I just define all 20 of these functions in this same “MessageListener” class, or do I write a separate class to keep all of these functions?

您可能应该将它们放在不同的 class 上。如果将它们放在 MessageListener 上,那么您将很难测试、重构和维护代码,因为您的协议逻辑与应用程序逻辑紧密耦合。

定义一个显式接口,MessageListener 使用它来调度表示网络操作的高级事件。然后为您的特定应用程序适当地实现该接口。稍后,您可以为另一个应用程序以不同的方式实现它。或者你可以写测试替身。或者您可以在不更改应用程序逻辑的情况下更改协议。与将两者合二为一相比,将这两部分解耦可为您提供更多的灵活性 class.

I might get 10 messages at about the same time, and they may need to call the same function, so I wasn’t sure the best architecture approach here.

我认为这是正交的,但如果它是您的协议或应用程序逻辑中足够重要的部分,您可能需要考虑在我上面提到的显式接口中构建某种矢量化。而不是 appObj.doOneThing(oneThing) 也许你有 appObj.doSeveralThings([oneThing, anotherThing]).

I also want to build a GUI to interact with the server for some troubleshooting and monitoring on occasion. I’m familiar with Tkinter and it would be fine for this, and I can write the GUI in a separate file and have it connect to the server over a socket as well. But would I use the same socket listener implemented above and just pass it similar messages? Or should I build a separate class and factory to listen for the GUI connections?

我想这取决于您想执行的交互。如果这是一个与普通客户端相比具有额外权限的 GUI,则您需要具有身份验证和授权功能的协议和服务器,或者您不能使用相同的服务器和协议,因为您可能会给客户端提供这些额外权限。

如果您只想使用一种易于调试的界面来模拟真实的客户端,该界面可以让您以客户端通常与之交互的方式轻松地与服务器交互,但界面可以让这更容易你,你可以(大概应该)连接到同一台服务器。