Python twisted - 需要遍历所有连接寻找客户端

2 投票
1 回答
1836 浏览
提问于 2025-04-16 22:07

我正在尝试创建一个简单的代金券程序。

客户端连接到服务器,询问代金券是否还有剩余时间,如果有,服务器会回复剩余的时间。

我可以控制服务器和客户端,客户端的代码也是我写的。

现在这是我在服务器端的代码,客户端的部分比较简单明了。

这个程序的一个大问题是,如果两个客户端使用相同的代金券代码连接,它们都会获得访问权限,因为服务器并没有检查是否有其他活跃的客户端在使用这个代码。

有没有人能解释一下,或者提供一些文档,告诉我该怎么解决这个问题?

#!/usr/bin/env python

from twisted.internet import reactor, protocol

class Responder(protocol.Protocol):

    def dataReceived(self, data):
        # check the voucher code, and return disabled if its out of time or not there. Otherwise return time left.
        if data.startswith("check="):
            param, vcode = data.split("=")
            checkcode = SQLConnect("check", vcode, vcode)
            if checkcode == "disabled":
                self.transport.write("disabled")
            else:
                self.transport.write(str(checkcode))
        # Update time left.
        if data.startswith("update="):
            param, vcode, vtime = data.split("=")
            SQLConnect("update", vcode, vtime)

def main():
    factory = protocol.ServerFactory()
    factory.protocol = Responder
    reactor.listenTCP(6500,factory)
    reactor.run()

if __name__ == '__main__':
    main()

1 个回答

6

如果一个代金券在客户检查时变成“正在使用”,然后在客户断开连接时变成未使用,听起来你只需要维护一组代金券。当检查完成时把代金券加进去,客户断开时把它们移除。你可以把这些代金券放在工厂里,这样所有的客户连接都可以共享。比如:

#!/usr/bin/env python

from twisted.internet import reactor, protocol

class Responder(protocol.Protocol):
    def connectionMade(self):
        self.vcode = None

    def dataReceived(self, data):
        # check the voucher code, and return disabled if its out of time or not there. Otherwise return time left.
        if data.startswith("check="):
            param, vcode = data.split("=")
            if vcode in self.factory.activeVouchers:
                self.transport.write("in use")
                return
            self.factory.activeVouchers.add(vcode)
            self.vcode = vcode

            checkcode = SQLConnect("check", vcode, vcode)
            if checkcode == "disabled":
                self.transport.write("disabled")
            else:
                self.transport.write(str(checkcode))
        # Update time left.
        if data.startswith("update="):
            param, vcode, vtime = data.split("=")
            SQLConnect("update", vcode, vtime)

    def connectionLost(self, reason):
        if self.vcode is not None:
            self.factory.activeVouchers.remove(self.vcode)

def main():
    factory = protocol.ServerFactory()
    factory.activeVouchers = set()
    factory.protocol = Responder
    reactor.listenTCP(6500,factory)
    reactor.run()

if __name__ == '__main__':
    main()

Responder上新增的vcode属性可以在客户断开连接时更新activeVouchers集合(这会触发Responder.connectionLost调用)。在Responder.dataReceived的开头附近增加的检查会在代金券被使用时把它们加入集合,并防止任何正在使用的代金券被领取。

除此之外,你可能还想考虑其他几个方面。首先,你可能应该使用twisted.protocols.basic.LineOnlyReceivertwisted.protocols.basic中的其他协议,而不是单纯使用ProtocoldataReceived会在网络上收到任何字节时被调用。由于TCP是面向流的传输,而不是面向消息的传输,你可能会遇到像dataReceived("chec")紧接着dataReceived("k=somecode")这样的情况。按照你现在实现的dataReceived,这种情况不会被处理,客户将不会收到任何响应。LineOnlyReceiver增加了基于行的分帧,这样像“check=somecode\r\n”这样的字节可以被解释为完整的消息,并且“check=somecode”会在一次lineReceived调用中全部传递。

其次,SQLConnect看起来可能会执行一些阻塞的输入输出操作。如果是这样的话,你的服务器在处理多个客户请求时可能会表现不佳,因为任何阻塞都会阻止所有新事件的处理,包括来自不同客户的事件。你可能想看看twisted.enterprise.adbapi,它提供了一个非阻塞的SQL API。

撰写回答