最近有些事儿比较忙,python的学习就断断续续,这个练习来得比预期的晚,不过还好,不管做什么,我都希望能认真对待,认真做好每一件事。

引入

这个练习原书中称作“使用XML-RPC进行文件共享”,题目是从使用的技术介绍的,做完这个练习之后我觉得最终其实实现的是一个迅雷的雏形——一个P2P下载器。当然了实际使用中的下载工具远远比这个要复杂,包括断点续传,断网重连等等。

那么我们学习的方法也就明确了,使用到了P2P的概念和XML-RPC的原理,那么就从这两个开始吧。

关于P2P

额,这里不进行官方的概念介绍,结合这个练习说明P2P的大体原理。使用P2P之后,我们查找一个共享资源的大体流程如下

# P2P协议的理解,弱化了server-client的概念,在网络中,每个节点都可能是server,也可能是client,暂且把发起请求的称为client,处理请求的称为server

1. client向服务器A发起寻找文件test的请求
2. A现在自己的目录下面寻找是否有test
3. 如果有则返回该文件给client,程序结束
4. 如果A服务器没有,则A遍历在线节点列表
5. 如果找到,返回给client,结束
6. 如果没有,返回空

由上的过程看出网络中每个节点需要具备以下功能:

# 每个节点需要维护的资源
共享目录:在网络中存在n台机器,每台服务器都有一个可供其他服务器访问的目录(目录下面有各种文件)
在线节点列表:每天机器都有一个所有愿意共项目录的服务器列表 # 服务器的功能
服务器应该能更新在线节点列表
如果在线节点很多,是不是应该一直寻找下去(设置一个查找深度)
服务器查询某个文件是否存在
服务器可以下载某一个文件到自己的共享目录

很明白了,在P2P网络中每个节点可以为别人提供资源,也可以向其他节点请求资源,对于每个节点来说就是共享出自己的资源,然后知道去哪里请求资源(需要一个其他节点的列表)。

关于xml-rpc

rpc:远程过程调用(之前学习分布式操作系统的时候想不到远程过程调用是什么,使用场景是什么,但是相信总起到了我意识到或者没意识到的作用),心爱那个心爱那个其实和http协议也有类似的地方,都是请求其他计算机的资源,并不关心在其他计算机上是怎么实现的。其原理也就是

# xml_rpc原理
在server A定义方法method
在client使用A的proxy调用method

当然了,rpc还有更详细的协议规范,但是这一层都被xml-rpc通过封装屏蔽了。这里也不作进一步探寻了。

具体实现

主要有两个模块,client和server,主要功能在server中实现,client只是对server调用进行了一些封装。

server.py

#!/usr/bin/env python
# -*- coding=utf-8 -×- from xmlrpclib import ServerProxy, Fault
from os.path import join, isfile, abspath
from SimpleXMLRPCServer import SimpleXMLRPCServer
from urlparse import urlparse
import sys MAX_HISTORY_LENGTH = 6
SimpleXMLRPCServer.allow_reuse_address = 1 UNHANDLED = 100
ACCESS_DENIED = 200 class UnhandledQuery(Fault):
"""
表示无法处理的查询异常
"""
def __init__(self, message='Could not handle the query'):
Fault.__init__(self, UNHANDLED, message) class AccessDenied(Fault):
"""
在用户试图访问未被授权的资源时引发的异常
"""
def __init__(self, message='Access denied'):
Fault.__init__(self, ACCESS_DENIED, message) def inside(dir, name):
"""
判断访问的目录是否有限制访问的目录,限制非法目录访问,比如/var/www/../data
"""
dirs = abspath(dirs)
name = abspath(name)
return name.startswith(join(dirs, '')) def getPort(url):
"""
根据url获取端口号
"""
name = urlparse(url)[1]
parts = name.split(":")
return int(parts[-1]) class Node:
"""
P2P网络中的节点
"""
def __init__(self, url, directory, secret):
self.url = url
self.directory = directory
self.secret = secret
self.know = set() def query(self, query, history=[]):
"""
查找文件,先在本机查找,如果找到则返回,找不到则查找其他已知节点
"""
try:
print 'search local'
return self._handle(query)
except UnhandledQuery:
# 添加到history,标记查找的深度
history = history + [self.url]
if len(history) >= MAX_HISTORY_LENGTH:
raise
print 'search other server'
return self._broadcast(query, history) def hello(self, other):
"""
认识其他节点
"""
self.know.add(other)
return 0
def fetch(self, query, secret):
"""
用于从节点下载数据
"""
print 'server.fetch'
if secret != self.secret:
raise AccessDenied
result = self.query(query)
print 'result----',result
# 把查询到的数据写到本地
f = open(join(self.directory, query), 'w')
f.write(result)
f.close()
return 0 def _start(self):
"""
内部使用,用于启动XML_RPC服务器
"""
server = SimpleXMLRPCServer(("", getPort(self.url)), logRequests=False)
server.register_instance(self)
server.serve_forever() def _handle(self, query):
"""
搜索文件在本服务器上是否存在
"""
dirs = self.directory
name = join(dirs, query)
if not isfile(name):
raise UnhandledQuery
return open(name).read() def _broadcast(self, query, history):
"""
内部时使用,用于将查询广播到其他已知节点
"""
for other in self.know.copy():
try:
# 根据url创建远程节点的proxy,使用xml_rpc进行远程调用
print 'search ----', other
server = ServerProxy(other)
print 'start query', other
return server.query(query, history)
except Fault, f:
if f.faultCode == UNHANDLED:
pass
else:
self.know.remove(other)
except:
# 说明该server已经不可用
self.know.remove(other)
# 如果在所有节点中没有找到,就返回空
raise UnhandledQuery def main():
url, directory, secret = sys.argv[1:]
node = Node(url, directory, secret)
node._start() if __name__ == '__main__':
main()

server.py

client.py

#! /usr/bin/env python
# -*- coding=utf-8 -*- from xmlrpclib import ServerProxy, Fault
from cmd import Cmd
from random import choice
from string import lowercase
from server import Node, UNHANDLED
from threading import Thread
from time import sleep
import sys HEAD_START = 0.1 # second
SECRET_LEN = 100 def randomStr(len):
"""
生成指定长度的字符串
"""
chars = []
letters = lowercase[:26]
while len > 0:
len = len - 1
chars.append(choice(letters))
return ''.join(chars) class Client1(Cmd):
"""
Node基于文本的界面
""" def __init__(self, url, dirname, urlfile):
"""
初始化Cmd
随机生成秘钥
启动服务器
启动字符界面
"""
print 'in'
Cmd.__init__(self)
self.secret = randomStr(SECRET_LEN)
n = Node(url, dirname, self.secret)
t = Thread(target=n._start)
t.setDaemon(1)
t.start()
# 等待服务器启动
sleep(HEAD_START)
self.server = ServerProxy(url)
for line in open(urlfile):
line = line.strip()
self.server.hello(line) def do_fetch(self, arg):
"调用服务器的fetch方法"
try:
print 'do_fetch'
self.server.fetch(arg, self.secret)
except Fault, f:
print f
if f.faultCode != UNHANDLED:
raise
print 'Could not find the file ', arg def do_exit(self, arg):
"退出程序"
print
sys.exit() do_EOF = do_exit # 接收到EOF的时候也退出程序
# 设置提示符
prompt = '>' def main():
urlfile, directory, url = sys.argv[1:]
print urlfile, directory, url
print '---------'
print dir()
client = Client1(url, directory, urlfile)
client.cmdloop() if __name__ == '__main__':
main()

client.py

总结

在运行这个练习的时候,要注意死锁,因为有共享资源的问题

# 线程死锁
server A的url list里面有Server B
server B的url list里面有Server A
当A请求B的时候,B发现自己也没有,B根据url list查询其他server,这个时候根据list需要去查询A
但是这个时候A正在等待B的回应,所以就造成了
A等B,B等A的死锁情况 # 解决死锁
每个server B在向url list里面的server A发出请求之前判断本神是否是Server A正在请求B

完整代码

http://pan.baidu.com/s/1i58mk3V

python练习七—P2P下载的更多相关文章

  1. Python在七牛云平台的应用(三)简单的人脸识别

    前言 这是最后一篇介绍python在七牛云平台的应用了,因为-前两篇文章第一篇分享了怎么安装七牛的官方库以及怎么对自己的空间进行下载上传,删除等行动.而第二篇则分享了怎么利用七牛的API接口,由于七牛 ...

  2. Python在七牛云平台的应用(二)图片瘦身

    (一)七牛云平台的图片瘦身功能简介:(引用自官网) 针对jpeg.png格式图片 瘦身后分辨率不变,格式不变. 肉眼画质不变. 图片体积大幅减少,节省 CDN 流量 官网给的图片压缩率很高,官网给的「 ...

  3. Python实现七牛云视频播放

    这篇文章是使用Python的Web框架Django Rest Framework来提供视频相关的api接口,主要功能包括视频上传.视频转码.视频访问授权.删除视频文件.视频截图功能. 七牛云上的基本概 ...

  4. Python学习—Anaconda详细 下载、安装与使用,以及如何创建虚拟环境,不仅仅只有安装步骤哦

    上一期我们介绍了Python.Pycharm.Anaconda三者之间的关系以及使用,这一期主要详细介绍如何在Windows上下载和安装工具Anaconda,然后使用其自带的conda管理不同项目的虚 ...

  5. Windows版的各种Python库安装包下载地址与安装过程

    在用Python开发时(Windows环境),会碰到需要安装某个版本的第三方库,为了以后查找.安装方便,总结如下: windows版的各种Python库安装包下载地址:http://www.lfd.u ...

  6. windows使用python调用wget批处理下载数据

    wget是linux/unix下通常使用的下载http/ftp的数据,使用非常方便,其实wget目前经过编译,也可在windows下使用.最近需要下载大量的遥感数据,使用了python写了批处理下载程 ...

  7. python多进程断点续传分片下载器

    python多进程断点续传分片下载器 标签:python 下载器 多进程 因为爬虫要用到下载器,但是直接用urllib下载很慢,所以找了很久终于找到一个让我欣喜的下载器.他能够断点续传分片下载,极大提 ...

  8. python之花瓣美女下载

    python之花瓣美女下载 作者:vpoet mail:vpoet_sir@163.com 注:代码随意copy 不用告诉我 主要功能:     1.搜索花瓣http://huaban.com/下的图 ...

  9. Python之FTP多线程下载文件之分块多线程文件合并

    Python之FTP多线程下载文件之分块多线程文件合并 欢迎大家阅读Python之FTP多线程下载系列之二:Python之FTP多线程下载文件之分块多线程文件合并,本系列的第一篇:Python之FTP ...

随机推荐

  1. 第四范式涂威威:AutoML技术现状与未来展望

    以下内容是对AutoML技术现状与未来展望讲座的总结. 1.机器学习定义 <西瓜书>中的直观定义是:利用经验来改善系统的性能.(这里的经验一般是指数据) Mitchell在<Mach ...

  2. Maven之pom.xml配置文件详解

    此文非原创,摘自:https://www.baidu.com/link?url=GlGgW21nijIiULDZj0RfPH8ofqGMqEnAzXiym7O3hfrZM5nFH2enukemBNTX ...

  3. 在JAVA中对于类,对象,继承,多态的看法

    这是我第一次学高级语言.很抱歉选择了JAVA,目标是开发一款可以上线的APP. 类:是建立对象的一个模板,就是系列产品中的基础款图纸.只是图纸而已.不是产品. 对象:是一个可以操作的对象.新建一个对象 ...

  4. windows 2008解决120天授权过期问题(亲测可用)

    https://blog.csdn.net/tladagio/article/details/80503198 最后的注册号码可以是:就是那个注册号码:5296992 4954438 6565792. ...

  5. Java中十六进制转换 Integer.toHexString()

    为了显示一个byte型的单字节十六进制(两位十六进制表示)的编码,请使用: Integer.toHexString((byteVar & 0x000000FF) | 0xFFFFFF00).s ...

  6. JavaScript-BOM与DOM

    BOM与DOM BOM: Browser Object Model(浏览器对象模型),即把 浏览器 当做一个对象来看待.BOM 除了可以访问文档中的组件之外,还可以访问 浏览器组件,比如页面中的 na ...

  7. Particle Filters

    |—粒子滤波原理 |—基础代码的建立—|—前进 |                               |—转弯 |                               |—噪音(误差 ...

  8. Sqlalchemy python经典第三方orm

    Ⅰ. 安装 pip install sqlalchemy Ⅱ. 起步链接 import time import threading import sqlalchemy from sqlalchemy ...

  9. offset系列、scroll系列与client系列

    offset系列: offsetLeft:获取元素距离最左边的距离,自身的margin包括在内,不包括自身的border offsetTop:获取元素距离最上边的距离,自身的margin包括在内,不包 ...

  10. linux网络编程-CRC校验

    1.CRC校验是什么?为什么要使用CRC校验? CRC 即 循环冗余校验  是一种差错检测方法,可以做到对帧的无差错接受 因为现实的通信链路都不会是理想的,比特在传输的过程中有可能出现差错, 为了保证 ...