前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >使用XML-RPC共享文件(2):再次实现

使用XML-RPC共享文件(2):再次实现

作者头像
不可言诉的深渊
发布2019-07-26 16:36:39
9240
发布2019-07-26 16:36:39
举报

使用XML-RPC共享文件(1):初次实现

5.再次实现

初次实现存在很多缺陷和缺点,这里不打算列出全部,而只列出几个重要的。

  • 如果你停止并重启一个节点,可能出现错误消息,指出端口被占用。
  • 你可能想提供对用户更友好的界面,而不是在交互式Python解释器中使用xmlrpc.client。
  • 返回的编码不方便,一种更自然,更符合Python风格的解决方案是,在找不到文件时引发自定义异常。
  • 节点没检测它返回的文件是否包含在目录中。通过使用诸如'../somesecretfile.txt'这样的路径,图谋不轨的黑客能够非法访问节点的其他任何文件。
  • 第一个问题很好解决,只需将SimpleXMLPRCServer的属性allow_reuse_address设置为True即可。

SimpleXMLRPCServer.allow_reuse_address = 1

如果你不想直接修改这个类,可创建其子类。其他几个问题解决起来要复杂些,将在接下来的几小节分别讨论。

5.1.创建客户端界面

客户端界面是使用模块cmd中的Cmd类实现的,有关其工作原理的详细信息,请参阅“Python库参考手册”。简单地说,你从Cmd派生出一个子类来创建一个命令行界面,同时对于要让它能够处理的每个命令(如foo),都创建一个方法(如do_foo)。这个方法将命令行余下的内容(一个字符串)作为其唯一的参数。例如,如果你在命令行界面输入以下内容:

say hello

将调用方法do_say,并将字符串'hello'作为其唯一的参数。Cmd的子类使用什么样的提示符取决于属性prompt。

这里的界面将只实现fetch(下载文件)和exit(退出程序)。命令fetch调用服务器的方法fetch,并在文件没有找到时打印一条错误信息。命令exit打印一个空行(这只是出于美观考虑)并调用sys.exit。(EOF表示已到达文件末尾。在UNIX系统中,用户按下Ctrl+D时将执行这个命令。)

然而,在构造函数中需要做什么呢?你希望每个客户端都与其对等体关联起来。为此,可创建一个Node对象并调用其方法_start,但如果这样做,客户端在方法_start返回前什么都做不了,这导致客户端毫无用处。为解决这个问题,可在一个独立的线程中启动Node。通常,使用线程时需要使用锁等机制做大量的防护和同步工作。然而,由于Client只通过XML-RPC与其Node交互,你无需做任何防护和同步工作,要在独立的线程中运行方法_start,只需将下面的代码放在程序的某个合适位置:

from threading import Thread

n = Node(url, dirname, self.secret)

t = Thread(target=n._start)

t.start()


警告 修改这个项目的代码务必小心。Client开始与Node对象直接交互(或相反)后,很容易出现与线程化相关的问题。修改代码前,务必完全理解线程化。


为确保你使用XML-RPC连接到它时已完全启动,先启动服务器,再使用teme.sleep等待一段时间。

然后,遍历一个包含url文件的所有行,并使用方法hello将服务器介绍给这些行表示的对等体。

你不用自己去设置密码,可使用自定义函数random_string,它生成一个由Client和Node共享的随机密码字符串。

5.2.引发异常

不返回表示成功还是失败的编码,而是假定肯定会成功,并在失败时引发异常。在XML-RPC中,异常(或故障)是使用数字标识的。在这个项目中,我随意的选择了100和200这两个数,分别用于表示正常的失败(请求未得到处理)和请求被拒绝(拒绝访问)。

异常是xmlrpc.client.Fault的子类。在服务器中引发的异常将传递到客户端,并保持faultCode不变。如果在服务器中引发了普通异常(如IOError),也将创建一个Fault类实例,因此你不能在服务器中随意使用异常。

5.3.验证文件名

需要处理的最后一个问题是,检查指定的文件是否包含在指定的目录中。这样做的方法有很多,但为独立于平台(即适用于Windows、UNIX和macOS),应使用模块os.path。

这里采用的简单方法如下:根据目录名和文件名创建绝对路径(例如,这将把'/foo/bar/../baz'转换为'/foo/baz'),将目录名与空文件名合并以确保它以文件分隔符(如'/')结尾,再检查绝对文件名是否以绝对路径打头。如果是这样的,就说明指定的文件包含在指定的目录中。

再次实现的完整源代码如图所示。

5.4.尝试使用

下面来看看如何使用这个程序。首先向下面这样启动它:

python client.py urls.txt directory http://localhost:4242

文件urls.txt里的每行应包含一个URL,即包含其他所有已知对等体的URL。通过第二个参数指定的目录应包含要共享的文件(新文件也将下载到这个目录)。最后一个参数是对等体的URL。运行这个命令时,将出现类似于下面的提示符:

>

下面来尝试获取一个不存在的文件:

通过(在同一台计算机的不同端口或不同计算机上)启动几个相互认识的节点(为确保这些节点相互认识,只要将它们的URL都放在URL文件即可),可尝试像使用第一个原型那样使用这个程序。玩烦了后,再接着阅读下一节。

6.进一步探索

对于这个系统,你可能会想出多种改进和扩展方式。下面是以下探索建议。

  • 添加缓存功能。在节点通过调用query来传递文件时,为何不同时存储该文件呢?这样,再有人请求这个文件时,响应速度将更快。你可以设置最大缓存空间,删除最早缓存的文件等。
  • 使用线程化(异步)服务器。(这有点难。)这样,可向多个节点寻求帮助,而无需等待它们应答(他们将在以后调用方法reply来应答)。
  • 支持更高级的查询,如查询文本文件的内容。
  • 更充分地利用方法hello。通过调用hello发现新节点时,为何不将这个新节点介绍给其他所有已知的对等体呢?或许你还能想到更巧妙的新对等体发现方式。
  • 深入研究用于分布式系统的表述性状态传递(REST)理念。REST可用于替代XML-RPC等Web服务技术,详情请参阅http://en.wikipedia.org/wiki/REST。
  • 使用xmlrpc.client.Binary来封装文件,从而更安全的传输非文本文件。
  • 阅读SimpleXMLRPCServer的代码。研究DocXMLRPCServer类以及libxmlrpc中的多调用(multicall)扩展。
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-08-15,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Python机器学习算法说书人 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档