Python 扭曲 - 需要遍历所有连接并找到客户端

发布于 2024-11-25 14:12:47 字数 1167 浏览 1 评论 0原文

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

客户端连接到服务器并询问凭证是否还有剩余时间,如果是,服务器会响应多少时间。

我控制服务器和客户端,客户端也由我编码。

现在这就是我的服务器端的内容,客户端是不言自明的。

其中最大的缺陷是,如果 2 个客户端使用相同的优惠券代码连接,它们都将具有访问权限,因为服务器不会检查是否存在具有该代码的活动客户端。

任何人都可以解释或提供有关这如何可能的文档吗?

#!/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()

I am trying to create a simple voucher program.

Client connects to server and asks if a voucher has time left on it, if yes the server responds with how much time.

I have control of the server and the clients, the client side is coded by me as well.

Right now this is what I have for my server side, the client side is self explanatory.

The big flaw in this, is that if 2 clients connect with the same voucher code, they will both have access because the server is not checking if there is an active client with that code.

Could anyone explain or lead to documentation in how this is possible ?

#!/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()

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

我的奇迹 2024-12-02 14:12:47

如果凭证在客户端检查时变为“正在使用”,然后在客户端断开连接时变为未使用,听起来您只需要保留一组凭证,在检查完成时添加到其中,并在客户端断开连接时将其删除。您可以将其保留在工厂中,以便在所有客户端连接之间共享。例如:

#!/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 中的其他协议之一,而不仅仅是 Protocol 。每当通过网络接收到任何字节时都会调用 dataReceived 。由于 TCP 是面向流的传输而不是面向消息的传输,因此您最终可能会得到类似 dataReceived("chec") 的调用,紧接着是 dataReceived("k=somecode" )。由于您的 dataReceived 现已实现,因此不会处理这种情况,客户端将不会收到任何响应。 LineOnlyReceiver 添加了基于行的帧,因此“check=somecode\r\n”之类的字节可以解释为完整的消息,并且“check=somecode”可以在单个 lineReceived 呼叫。

其次,SQLConnect 看起来可能会执行一些阻塞 I/O。如果这是真的,则意味着您的服务器无法很好地处理并发客户端请求,因为任何阻塞都会阻止处理所有新事件,包括来自不同客户端的事件。您可能想查看一下 twisted.enterprise.adbapi 的非阻塞 SQL API。

If a voucher becomes "in use" when a client checks it and then becomes unused when the client disconnects, it sounds like you just need to keep a set of vouchers which you add to when the check is done and remove from when the client disconnects. You could keep this on the factory so it is shared between all client connections. For example:

#!/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()

The new vcode attribute on Responder lets the activeVouchers set get updated when the client disconnects (which triggers the Responder.connectionLost call). The additional check near the beginning of Responder.dataReceived adds vouchers to that set when they become used and prevents any in use voucher from being claimed.

Apart from that, there are a couple other things you might want to consider. First, you should probably use twisted.protocols.basic.LineOnlyReceiver or one of the other protocols in twisted.protocols.basic instead of just Protocol. dataReceived is called whenever any bytes are received over the network. Since TCP is a stream-oriented transport rather than a message-oriented transported, you may end up with a call like dataReceived("chec") quickly followed by dataReceived("k=somecode"). As your dataReceived is implemented now, this case won't be handled and the client will receive no response. LineOnlyReceiver adds line-based framing, so that bytes like "check=somecode\r\n" can be interpreted as a complete message and "check=somecode" delivered all at once, in a single lineReceived call.

Second, SQLConnect looks like it probably performs some blocking I/O. If this is true, it means that your server won't handle concurrent client requests very well, since any blocking prevents all new events from being handled, including those from different clients. You might want to take a look at twisted.enterprise.adbapi for a non-blocking SQL API.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文