【问题标题】:Proper use of a client and Deferred with Twisted正确使用客户端并使用 Twisted 延迟
【发布时间】:2013-07-17 08:56:38
【问题描述】:

我使用socket 实现了一个基本的 SOCKS4 客户端,但我的 Twisted 翻译不太顺利。这是我当前的代码:

import struct
import socket

from twisted.python.failure import Failure

from twisted.internet import reactor
from twisted.internet.defer import Deferred
from twisted.internet.protocol import Protocol, ClientFactory

class Socks4Client(Protocol):
    VERSION = 4
    HOST = "0.0.0.0"
    PORT = 80

    REQUESTS = {
        "CONNECT": 1,
        "BIND": 2
    }

    RESPONSES = {
        90: "request granted",
        91: "request rejected or failed",
        92: "request rejected because SOCKS server cannot connect to identd on the client",
        93: "request rejected because the client program and identd report different user-ids"
    }

    def __init__(self):
        self.buffer = ""

    def connectionMade(self):
        self.connect(self.HOST, self.PORT)

    def dataReceived(self, data):
        self.buffer += data

        if len(self.buffer) == 8:
            self.validateResponse(self.buffer)

    def connect(self, host, port):
        data = struct.pack("!BBH", self.VERSION, self.REQUESTS["CONNECT"], port)
        data += socket.inet_aton(host)
        data += "\x00"

        self.transport.write(data)

    def validateResponse(self, data):
        version, result_code = struct.unpack("!BB", data[1:3])

        if version != 4:
            self.factory.protocolError(Exception("invalid version"))
        elif result_code == 90:
            self.factory.deferred.callback(self.responses[result_code])
        elif result_code in self.RESPONSES:
            self.factory.protocolError(Exception(self.responses[result_code]))
        else:
            self.factory.protocolError(Exception())

        self.transport.abortConnection()

class Socks4Factory(ClientFactory):
    protocol = Socks4Client

    def __init__(self, deferred):
        self.deferred = deferred

    def clientConnectionFailed(self, connector, reason):
        self.deferred.errback(reason)

    def clientConnectionLost(self, connector, reason):
        print "Connection lost:", reason

    def protocolError(self, reason):
        self.deferred.errback(reason)

def result(result):
    print "Success:", result

def error(reason):
    print "Error:", reason

if __name__ == "__main__":
    d = Deferred()
    d.addCallbacks(result, error)

    factory = Socks4Factory(d)
    reactor.connectTCP('127.0.0.1', 1080, factory)

    reactor.run()
  1. 我感觉我在滥用Deferred。这是从我的客户发送结果的正确方式吗?
  2. 我已经阅读了一些教程,查看了文档,并阅读了与 Twisted 捆绑的大多数协议,但我仍然无法弄清楚:ClientFactory 到底是什么?我是否以正确的方式使用它?
  3. clientConnectionLosts 经常被触发。有时我会失去连接并获得成功的响应。怎么会这样?这是什么意思,我应该将其视为错误吗?
  4. 如何确保我的延迟调用只有一个回调/错误返回?

感谢任何提示。

【问题讨论】:

    标签: python twisted deferred


    【解决方案1】:

    我觉得我在滥用 Deferred。这是从我的客户发送结果的正确方式吗?

    这并不理想,但也不完全是错误的。通常,您应该尝试使实例化Deferred 的代码尽可能接近在Deferred 上调用Deferred.callbackDeferred.errback 的代码。在这种情况下,这些代码片段相距甚远——前者位于__main__ 中,而后者位于由__main__ 中的代码创建的类创建的类中。这有点像得墨忒耳定律——这两件事之间的步骤越多,软件的耦合越紧密、不灵活和脆弱。

    考虑为Socks4Client 提供一个创建并返回此Deferred 实例的方法。然后,尝试使用端点来设置连接,以便您可以更轻松地调用此方法:

    endpoint = TCP4StreamClientEndpoint(reactor, "127.0.0.1", 1080)
    d = endpoint.connect(factory)
    def connected(protocol):
        return protocol.waitForWhatever()
    d.addCallback(connected)
    d.addCallbacks(result, error)
    

    这里要注意的一点是,使用端点时,不会调用工厂的 clientConnectionFailedclientConnectionLost 方法。端点接管了前者的责任(虽然不是后者)。

    我已经阅读了一些教程,查看了文档,并通读了大多数与 Twisted 捆绑的协议,但我仍然无法弄清楚:ClientFactory 到底是做什么用的?我用对了吗?

    这只是为了你正在做的事情。 :) 它创建用于连接的协议实例。工厂是必需的,因为您可能会创建与许多服务器的连接(或与一台服务器的许多连接)。然而,很多人对ClientFactory 有疑问,所以最近引入的 Twisted API 不再依赖它。例如,您还可以将连接设置为:

    endpoint = TCP4StreamClientEndpoint(reactor, "127.0.0.1", 1080)
    d = connectProtocol(endpoint, Socks4Client())
    ...
    

    ClientFactory 现在不在了。

    clientConnectionLosts 经常被触发。有时我会失去连接并获得成功的响应。怎么会这样?这是什么意思,我应该将其视为错误吗?

    每个连接最终都必须丢失。您必须自行决定这是否是错误。如果你已经完成了你想做的所有事情并且你打电话给loseConnection,这可能不是一个错误。考虑到 HTTP 服务器的连接。如果您已经发送了请求并收到了回复,那么失去连接可能没什么大不了的。但如果你只收到了一半的回复,那就有问题了。

    我如何确保我的延迟调用只有一个回调/错误返回?

    如果您按照我在回答您上面的第一个问题时所描述的那样构建您的代码,那么执行此操作会变得更容易。当在 Deferred 上使用 callback/errback 的代码分布在程序的大部分区域时,正确执行此操作变得更加困难。

    不过,这只是适当的状态跟踪问题。一旦你给一个 Deferred 一个结果,你必须安排知道你不应该给它另一个结果。一个常见的习惯用法是删除对 Deferred 的引用。例如,如果您将其保存为协议实例的属性值,则在您为 Deferred 提供结果后将该属性设置为 None

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-07-24
      • 1970-01-01
      • 2020-10-19
      • 2018-03-17
      • 1970-01-01
      • 2011-10-17
      相关资源
      最近更新 更多