python练习七—P2P下载
最近有些事儿比较忙,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):
"退出程序"
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下载的更多相关文章
- Python在七牛云平台的应用(三)简单的人脸识别
前言 这是最后一篇介绍python在七牛云平台的应用了,因为-前两篇文章第一篇分享了怎么安装七牛的官方库以及怎么对自己的空间进行下载上传,删除等行动.而第二篇则分享了怎么利用七牛的API接口,由于七牛 ...
- Python在七牛云平台的应用(二)图片瘦身
(一)七牛云平台的图片瘦身功能简介:(引用自官网) 针对jpeg.png格式图片 瘦身后分辨率不变,格式不变. 肉眼画质不变. 图片体积大幅减少,节省 CDN 流量 官网给的图片压缩率很高,官网给的「 ...
- Python实现七牛云视频播放
这篇文章是使用Python的Web框架Django Rest Framework来提供视频相关的api接口,主要功能包括视频上传.视频转码.视频访问授权.删除视频文件.视频截图功能. 七牛云上的基本概 ...
- Python学习—Anaconda详细 下载、安装与使用,以及如何创建虚拟环境,不仅仅只有安装步骤哦
上一期我们介绍了Python.Pycharm.Anaconda三者之间的关系以及使用,这一期主要详细介绍如何在Windows上下载和安装工具Anaconda,然后使用其自带的conda管理不同项目的虚 ...
- Windows版的各种Python库安装包下载地址与安装过程
在用Python开发时(Windows环境),会碰到需要安装某个版本的第三方库,为了以后查找.安装方便,总结如下: windows版的各种Python库安装包下载地址:http://www.lfd.u ...
- windows使用python调用wget批处理下载数据
wget是linux/unix下通常使用的下载http/ftp的数据,使用非常方便,其实wget目前经过编译,也可在windows下使用.最近需要下载大量的遥感数据,使用了python写了批处理下载程 ...
- python多进程断点续传分片下载器
python多进程断点续传分片下载器 标签:python 下载器 多进程 因为爬虫要用到下载器,但是直接用urllib下载很慢,所以找了很久终于找到一个让我欣喜的下载器.他能够断点续传分片下载,极大提 ...
- python之花瓣美女下载
python之花瓣美女下载 作者:vpoet mail:vpoet_sir@163.com 注:代码随意copy 不用告诉我 主要功能: 1.搜索花瓣http://huaban.com/下的图 ...
- Python之FTP多线程下载文件之分块多线程文件合并
Python之FTP多线程下载文件之分块多线程文件合并 欢迎大家阅读Python之FTP多线程下载系列之二:Python之FTP多线程下载文件之分块多线程文件合并,本系列的第一篇:Python之FTP ...
随机推荐
- Fiddler功能介绍之Web抓包、远程抓包教程【转载】
一.fiddler简介 简单来说,Fiddler是一个http协议调试代理工具,它能够记录并检查所有你的电脑和互联网之间的http通讯.网上简介很多,我们不多说. 二.fiddler版本 fiddle ...
- Greenplum 日常维护手册 (汇总、点评、备查)
1. 数据库启动:gpstart常用可参数: -a : 直接启动,不提示终端用户输入确认-m:只启动master 实例,主要在故障处理时使用2. 数据库停止:gpstop:常用可参数:-a:直接停止, ...
- EFLinq查询
1.无参数查询var model = db.Database.SqlQuery<UserInfo>("select* from UserInfoes ").ToList ...
- Linux挂载NAS 网络附属存储
在工作中经常听到NAS,比如做数据交换,将数据从DB2数据库,导入到ORACLE数据库,采用BCP的方式,首先将DB2导出为文件,再从文件导入到ORACLE.那么中间需要一个很大的存储空间来保存从DB ...
- Jsp处理过程and数据交互
request处理客户端请求 客户端-------------->jsp页面--------------->服务器 常用方法 1.String getParameter(String na ...
- sftp修改用户home目录后登录时报connection closed by remote host
在sftp用户需要修改登录根目录的情况下,我们可以修改/etc/ssh/sshd_config文件中ChrootDirectory /home/[path]的路径. 但是,在重启sshd服务后,sft ...
- Tensoflow API笔记(N) 设备指定
tf.device是tf.Graph.device()的一个包装,是一个用于指定新创建的操作(operation)的默认设备的环境管理器.参数为device_name_or_function, ...
- mac 电脑下svn
mac点下使用的提交代码的方式是:eclipse + svn 的插件实现,总之中间遇到了很多的问题,不过还是都解决了. 前提是你已经安装了jdk. 安装的过程不再赘述直接上链接http://www.c ...
- 【python-appium】appium 关键字
Appium 服务关键字 关键字 描述 实例 automationName 你想使用的自动化测试引擎 Appium (默认) 或 Selendroid platformName 你要测试的手机操作系统 ...
- Redhat7配置ali-yum源
1.删除所有包 rpm -qa|grep yum|xargs rpm -e --nodeps 2.下载相关文件 下载地址 https://mirrors.aliyun.com/centos/7/os ...