扭曲的延迟回调中的奇怪行为

2 投票
1 回答
503 浏览
提问于 2025-04-17 02:30

目标:尝试与一系列服务器建立TCP连接,并打印出连接是否成功,以及是否提示输入密码。

问题:在我的回调函数(叫做connected)和错误回调函数(叫做failed)中,有一句代码(to_check -= 1)似乎从来没有被执行过,尽管这些函数中的打印语句是有输出的。

from twisted.internet import protocol, defer
import sys

class myProto(protocol.Protocol):
    got = ''
    def dataReceived(self,data):
        #print data
        self.got += data
        if "Password:" in data:
            self.transport.loseConnection()

    def connectionLost(self,reason):
        #print self.got
        if "Password:" in self.got:
            self.factory.success("and was prompted for password")
        else:
            self.factory.success("But was not prompted for password")

class myFactory(protocol.ClientFactory):
    protocol = myProto

    def __init__(self,deferred,host):
        self.deferred = deferred
        self.host = host
        print "Trying Connection to %s ..." % host

    def success(self,askpass):
        if self.deferred is not None:
            d, self.deferred = self.deferred , None
            d.callback([self.host,askpass])

    def clientConnectionFailed(self, connector, reason):
        if self.deferred is not None:
            d, self.deferred = self.deferred, None
            d.errback(reason)

def check_conn(host, port):
    from twisted.internet import reactor
    d = defer.Deferred()
    factory = myFactory(d,host)
    reactor.connectTCP(host,port,factory)
    return d

def main():

    ip = "10.1.1."
    port = 23
    total = 10
    to_check = total
    from twisted.internet import reactor
    def connected(whathappened):
        print >>sys.stdout, "Successfully connected to %s %s" % (whathappened[0],whathappened[1])
        to_check -= 1

    def failed(reason):
        print >>sys.stderr, "Connection to failed : %s" % reason
        to_check -= 1

    def checked(_):
        print >>sys.stdout, "%d connections left to check" % to_check
        if to_check == 0:
            reactor.stop()

    for i in range(0,total):
        d = check_conn(ip + str(i),port)
        d.addCallbacks(connected,failed)
        d.addBoth(checked)

    reactor.run()

if __name__ == "__main__":
    main()

输出:

Trying Connection to 10.1.1.0 ...
...
...
Trying Connection to 10.1.1.9 ...
Successfully connected to 10.1.1.1 and was prompted for password
10 connections left to check
Successfully connected to 10.1.1.0 and was prompted for password
10 connections left to check
Successfully connected to 10.1.1.2 and was prompted for password
10 connections left to check
Successfully connected to 10.1.1.9 and was prompted for password
10 connections left to check
....{Similar output}
Successfully connected to 10.1.1.6 and was prompted for password
10 connections left to check

应该检查的连接数量应该在减少,但它却保持不变。

1 个回答

8

这是关于你对Python中闭包理解的一般性问题;默认情况下,变量只在它被赋值的最内层函数中有效。-= 是一种隐式赋值,所以 to_check 变成了 connectedfailed 的局部变量。因此,main 函数中的 to_check 永远不会被改变。在Python 3.x中,在 connectedfailed 的顶部加上 nonlocal to_check 就能让它按你预期的那样工作。下面是一个在2.x中使用变更来实现相同功能的例子:

import itertools

def main():

    ip = "10.1.1."
    port = 23
    to_check = 10
    counter = itertools.count().next
    from twisted.internet import reactor
    def connected(whathappened):
        print >>sys.stdout, "Successfully connected to %s %s" % (whathappened[0],whathappened[1])
        return counter()

    def failed(reason):
        print >>sys.stderr, "Connection to failed : %s" % reason
        return counter()

    def checked(count):
        print >>sys.stdout, "%d connections left to check" % (to_check - count,)
        if count == to_check:
            reactor.stop()

    for i in range(0,total):
        d = check_conn(ip + str(i),port)
        d.addCallbacks(connected,failed)
        d.addBoth(checked)

    reactor.run()

撰写回答