5

我用 实现了一个基本的 SOCKS4 客户端socket,但是我的 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 捆绑的大多数协议,但我仍然无法弄清楚:a 到底是什么ClientFactory?我是否以正确的方式使用它?
  3. clientConnectionLosts 被触发了很多。有时我会失去连接并获得成功的响应。怎么会这样?这是什么意思,我应该将其视为错误吗?
  4. 如何确保我的延迟调用仅一个回调/错误返回?

任何提示表示赞赏。

4

1 回答 1

5

我有一种我在滥用 Deferred 的感觉。这是从我的客户发送结果的正确方法吗?

这并不理想,但也不完全错误。通常,您应该尝试使实例化 a 的代码Deferred尽可能接近调用Deferred.callbackorDeferred.errback的代码Deferred。在这种情况下,这些代码片段相距甚远 - 前者在,__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 的引用。例如,如果您将其保存为协议实例上的属性值,则将该属性设置为None当您为 Deferred 提供其结果时。

于 2013-07-17T11:42:51.177 回答