twisted:如何通过Perspective Broker发送和接收相同对象?

0 投票
1 回答
1244 浏览
提问于 2025-04-17 11:04

我有一个简单的“回声”PB客户端和服务器,客户端向服务器发送一个对象,服务器再把同样的对象发回客户端:

客户端:

from twisted.spread import pb
from twisted.internet import reactor
from twisted.python import util
from amodule import aClass

factory = pb.PBClientFactory()
reactor.connectTCP("localhost", 8282, factory)
d = factory.getRootObject()

d.addCallback(lambda object: object.callRemote("echo", aClass()))
d.addCallback(lambda response: 'server echoed: '+response)
d.addErrback(lambda reason: 'error: '+str(reason.value))
d.addCallback(util.println)

d.addCallback(lambda _: reactor.stop())
reactor.run()

服务器:

from twisted.application import internet, service
from twisted.internet import protocol
from twisted.spread import pb
from amodule import aClass

class RemoteClass(pb.RemoteCopy, aClass):
    pass
pb.setUnjellyableForClass(aClass, RemoteClass)

class PBServer(pb.Root):
    def remote_echo(self, a):
        return a

application = service.Application("Test app")

# Prepare managers
clientManager = internet.TCPServer(8282, pb.PBServerFactory(PBServer()));
clientManager.setServiceParent(application)

if __name__ == '__main__':
    print "Run with twistd"
    import sys
    sys.exit(1)

aClass是一个简单的类,它实现了可复制的功能:

来自twisted.spread的pb

class aClass(pb.Copyable):
    pass

当我运行上面的代码时,出现了这个错误:

twisted.spread.jelly.InsecureJelly: 模块 builtin 不被允许(在类型 builtin.RemoteClass中)。

实际上,对象在发送到服务器时没有任何问题,因为它在服务器端通过pb.setUnjellyableForClass(aClass, RemoteClass)进行了安全处理,但一旦返回给客户端,就会出现这个错误。

我在寻找一种简单的方法来在两个对等方之间发送和接收我的对象。

1 个回答

3

Perspective broker(PB)在网络上交流时,通过名字来识别类。一个类的名字部分来源于它被定义的模块。一个比较棘手的问题是,当你在命令行运行一个文件(也就是你的“主脚本”)时,类的名字可能会让你感到意外。比如,当你这样做的时候:

python foo.py

Python给foo.py中的代码的模块名并不是你想象中的"foo"。实际上,它的名字是类似于"__main__"的东西(这也是为什么if __name__ == "__main__":这个技巧能用的原因)。

但是,如果你应用的其他部分后来尝试从foo.py中导入某些东西,Python会重新评估它的内容,从而创建一个名为模块"foo"

此外,一个进程中定义在"__main__"模块里的类,可能和另一个进程中"__main__"模块里的类没有任何关系。在你的例子中,__main__.RemoteClass是在你的服务器进程中定义的,但在你的客户端进程的__main__模块中并没有RemoteClass

所以,PB就会搞混,无法完成对象的传输。

解决这个问题的方法是尽量减少主脚本中的代码量,特别是不要在这里定义任何名字(不定义类,也不定义函数)。

不过,另一个问题是,大家可能会期待RemoteCopy可以在PB中直接发送,而不需要额外的准备。其实,Copyable可以被发送,从而在对端创建一个RemoteCopy,但这并不是一个对称的关系。你的客户端也需要通过类似(或不同的)pb.setUnjellyableForClass调用来允许这一点。

撰写回答