我使用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()
Deferred
。这是从我的客户发送结果的正确方法吗?ClientFactory
究竟是什么?我用正确的方式使用它吗?clientConnectionLost
被触发了很多。有时我会失去联系并获得成功的回复。怎么会这样?这意味着什么,我应该将其视为错误吗?任何提示都表示赞赏。
答案 0 :(得分:5)
我有一种感觉,我在滥用延迟。这是从我的客户端发送结果的正确方法吗?
这不理想,但也不是完全错误的。通常,您应该尽量使实例化Deferred
的代码尽可能接近在Deferred.callback
上调用Deferred.errback
或Deferred
的代码。在这种情况下,这些代码片段相距很远 - 前者位于__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
方法。端点接管前者(尽管不是后者)。
我已经阅读了一些教程,查看了文档,并阅读了与Twisted捆绑在一起的大多数协议,但我仍然无法弄清楚:ClientFactory究竟是什么?我是以正确的方式使用它吗?
这就是你正在做的事情。 :)它创建用于连接的协议实例。需要工厂,因为您可能会创建与许多服务器(或与一个服务器的多个连接)的连接。但是,很多人都遇到了ClientFactory
的问题,因此最近推出的Twisted API并不依赖它。例如,您还可以将连接设置为:
endpoint = TCP4StreamClientEndpoint(reactor, "127.0.0.1", 1080)
d = connectProtocol(endpoint, Socks4Client())
...
ClientFactory
现在不在了。
clientConnectionLosts被触发了很多。有时我会失去联系并获得成功的回复。怎么会这样?这意味着什么,我应该将其视为错误吗?
每个连接最终都必须丢失。您必须自己决定这是否是错误。如果你已经完成了你想要做的所有事情并且你打电话给loseConnection
,那么这可能不是一个错误。考虑与HTTP服务器的连接。如果您已发送请求并收到回复,则丢失连接可能不是什么大问题。但如果你只收到一半的回复,那就是问题。
如何确保我的延迟调用只有一个回调/错误回复?
如果按照我上面的第一个问题的描述构建代码,那么执行此操作会变得更容易。当在Deferred上使用回调/错误的代码遍布程序的大部分时,那么正确地执行此操作变得更加困难。
但这只是适当的状态跟踪问题。一旦你推迟了结果,你必须安排知道你不应该给它另一个。一个常见的习惯用法是删除对Deferred的引用。例如,如果要将其保存为协议实例上的属性值,则在给出Deferred其结果时将该属性设置为None
。