python – 正确使用客户端并延迟使用Twisted

我用套接字实现了一个基本的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()

>我有一种感觉,我在滥用延迟.这是从我的客户端发送结果的正确方法吗?
>我已经阅读了一些教程,查看了文档,并阅读了与Twisted捆绑在一起的大多数协议,但我仍然无法弄清楚:ClientFactory到底是什么?我是以正确的方式使用它吗?
> clientConnectionLosts被触发了很多.有时我会失去联系并获得成功的回复.怎么会这样?这意味着什么,我应该将其视为错误吗?
>如何确保我的延迟调用只有一个回调/错误回复?

任何提示都表示赞赏.

解决方法:

I have a feeling that I’m abusing Deferred. Is this the right way to send results from my client?

它并不理想,但它也不是完全错误的.通常,您应该尝试将实例化Deferred的代码保持尽可能接近在Deferred上调用Deferred.callback或Deferred.errback的代码.在这种情况下,这些代码段相距很远 – 前者位于__main__中,而后者位于由__main__中的代码创建的类创建的类中.这有点像Demeter法则 – 这两件事之间的步骤越多,软件的耦合越紧密,越不灵活,越脆弱.

考虑为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)

这里需要注意的一点是使用端点,不会调用工厂的clientConnectionFailed和clientConnectionLost方法.端点接管前者(尽管不是后者).

I’ve read a few tutorials, looked at the documentation, and read through most of the protocols bundled with Twisted, but I still can’t figure it out: what exactly is a ClientFactory for? Am I using it the right way?

这就是你正在做的事情. :)它创建用于连接的协议实例.需要工厂,因为您可能会创建与许多服务器(或与一个服务器的多个连接)的连接.但是,很多人在使用ClientFactory时遇到了麻烦,因此最近推出的Twisted API并不依赖于它.例如,您还可以将连接设置为:

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

ClientFactory现在已经不在了.

clientConnectionLosts gets triggered a lot. Sometimes I lose the connection and get a successful response. How is that so? What does this mean, and should I treat it as an error?

每个连接最终都必须丢失.您必须自己决定这是否是错误.如果你已经完成了你想要做的所有事情并且你调用了lostConnection,那么这可能不是一个错误.考虑与HTTP服务器的连接.如果您已发送请求并收到回复,则丢失连接可能不是什么大问题.但如果你只收到一半的回复,那就是问题所在.

How do I make sure that my deferred calls only one callback/errback?

如果您按照上面第一个问题的描述构建代码,那么执行此操作会变得更容易.当在Deferred上使用回调/错误的代码分散在程序的大部分内容时,那么正确地执行此操作变得更加困难.

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

上一篇:Twisted模块示例


下一篇:jquery的deferred使用详解