如何使用Twisted创建非HTTP代理

1 投票
1 回答
1860 浏览
提问于 2025-04-16 19:08

我想用Twisted创建一个非HTTP的代理。我的目标是处理Terraria协议,这个协议完全是由二进制数据构成的。我注意到他们有一个内置的HTTP连接代理,但我的应用需要更像是一个入口点,能够转发到一个特定的服务器(有点像IRC中的BNC)。

我现在搞不清楚怎么从一个连接读取数据,然后把这些数据发送到另一个连接。

我已经尝试过用socket来完成这个任务,但因为需要同时保持两个连接活跃,所以阻塞的接收和发送方法效果不好。

1 个回答

6

在Twisted中,有几种不同的方法可以创建代理。基本的技术是通过对等连接,利用两个不同的协议,分别在两个不同的端口上,然后把它们连接起来,这样它们就可以互相交换数据。

最简单的代理是端口转发器。Twisted自带了一个端口转发器的实现,具体可以查看http://twistedmatrix.com/documents/current/api/twisted.protocols.portforward.html,里面有一些(文档不太详细的)类,比如ProxyClientProxyServer,不过实际的源代码在http://twistedmatrix.com/trac/browser/tags/releases/twisted-11.0.0/twisted/protocols/portforward.py上可能更有帮助。通过这些,我们可以看到Twisted中代理的基本技术:

def dataReceived(self, data):
    self.peer.transport.write(data)

当一个代理协议接收到数据时,它会把数据发送到另一边的对等方。就是这么简单!当然,通常你还需要一些额外的设置……接下来我们看看我之前写的一些代理。

这是一个针对Darklight的代理,这是我写的一个小型点对点系统。它与一个后端服务器进行通信,并且只想在数据不匹配预定义的头部时才进行代理。你可以看到它使用了ProxyClientFactory和端点(基本上是高级的ClientCreator)来开始代理,当它接收到数据时,有机会在继续之前检查数据,以决定是继续代理还是切换协议。

class DarkServerProtocol(Protocol):
    """
    Shim protocol for servers.
    """

    peer = None
    buf = ""

    def __init__(self, endpoint):
        self.endpoint = endpoint
        print "Protocol created..."

    def challenge(self, challenge):
        log.msg("Challenged: %s" % challenge)
        # ...omitted for brevity...
        return is_valid(challenge)

    def connectionMade(self):
        pcf = ProxyClientFactory()
        pcf.setServer(self)
        d = self.endpoint.connect(pcf)
        d.addErrback(lambda failure: self.transport.loseConnection())

        self.transport.pauseProducing()

    def setPeer(self, peer):
        # Our proxy passthrough has succeeded, so we will be seeing data
        # coming through shortly.
        log.msg("Established passthrough")
        self.peer = peer

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

        # Examine whether we have received a challenge.
        if self.challenge(self.buf):
            # Excellent; change protocol.
            p = DarkAMP()
            p.factory = self.factory
            self.transport.protocol = p
            p.makeConnection(self.transport)
        elif self.peer:
            # Well, go ahead and send it through.
            self.peer.transport.write(data)

这是一段相对复杂的代码,它将两个StatefulProtocol强行连接在一起。这段代码来自一个VNC代理(具体来说是https://code.osuosl.org/projects/twisted-vncauthproxy),它需要在准备连接之前进行很多预认证的操作。这种代理是最复杂的;为了速度,你不想与通过代理的数据进行交互,但你需要提前做好一些设置。

def start_proxying(result):
    """
    Callback to start proxies.
    """

    log.msg("Starting proxy")
    client_result, server_result = result
    success = True
    client_success, client = client_result
    server_success, server = server_result

    if not client_success:
        success = False
        log.err("Had issues on client side...")
        log.err(client)

    if not server_success:
        success = False
        log.err("Had issues on server side...")
        log.err(server)

    if not success:
        log.err("Had issues connecting, disconnecting both sides")
        if not isinstance(client, Failure):
            client.transport.loseConnection()
        if not isinstance(server, Failure):
            server.transport.loseConnection()
        return

    server.dataReceived = client.transport.write
    client.dataReceived = server.transport.write
    # Replay last bits of stuff in the pipe, if there's anything left.
    data = server._sful_data[1].read()
    if data:
        client.transport.write(data)
    data = client._sful_data[1].read()
    if data:
        server.transport.write(data)

    server.transport.resumeProducing()
    client.transport.resumeProducing()
    log.msg("Proxying started!")

所以,现在我已经解释完这些……

我还写了Bravo。也就是http://www.bravoserver.org/。所以我对Minecraft有一点了解,也因此对Terraria有些了解。你可能会想要解析通过代理传输的两边的数据包,所以你的实际代理可能一开始看起来像这样,但随着你开始理解你正在代理的数据,它会迅速演变。希望这些内容能帮助你入门!

撰写回答