您的位置:首页 > 编程语言 > Python开发

python基础 27章 使用XML_RPC进行文件共享

2017-06-02 00:55 525 查看
# *-*coding:utf-8 *-*

#python 3简单的Node实现 (simple_node.py)

from xmlrpc.client import ServerProxy

from os.path import join, isfile

from xmlrpc.server import SimpleXMLRPCServer

from urllib.parse import urlparse

import sys

def getPort(url):
'从url中获取节点'
name = urlparse(url)[1]
parts = name.split(':')
return int(parts[-1])

MAX_HISTORY_LENGTH = 6

OK = 1

FAIL = 2

EMPTY = ''

class Node:
"""
p2p中的网络节点
"""
def __init__(self, url, dirname, secret):
self.url = url
self.dirname = dirname
self.secret = secret
self.known = set()

def query(self, query, history=[]):
"""
查询文件可能向其他已知节点求助, 将文件作为字符串返回
"""
code, data = self._handle(query)
if code == OK:
return code, data
else:
history = history + [self.url]
if len(history) >= MAX_HISTORY_LENGTH:
return FAIL, EMPTY
return self._broadcast(query, history)

def hello(self, other):
"""
将节点添加到self.known中
"""
self.known.add(other)
return OK

def fetch(self, query, secret):
"""
寻找文件并且下载当前节点目录
"""
if secret != self.secret: return FAIL
code, data = self.query(query)
if code == OK:
f = open(join(self.dirname, query), 'w')
f.write(data)
f.close()
return OK
else:
return FAIL

def _start(self):
"""用于启动XML_PRC服务器"""
s = SimpleXMLRPCServer(("", getPort(self.url)), logRequests=True)
s.register_instance(self)
s.serve_forever()

def _handle(self, query):
"""isfile判断路径是否存在,如果存在返回OK和读取内容"""
dir = self.dirname
name = join(dir, query)
if not isfile(name): return FAIL, EMPTY
return OK, open(name).read()

def _broadcast(self, query, history):
"""
用hello方法加入的self.known中url查询文件
"""
for other in self.known.copy():
if other in history: return FAIL, EMPTY
try:
s = ServerProxy(other)
code, data = s.query(query)
if code == OK:
return  code, data
except:
self.known.remove(other)
return FAIL, EMPTY

def main():
url, dirname, secret = sys.argv[1:]
n = Node(url, dirname, secret)
n._start()

if __name__ == '__main__':
main()

"""

xmlrpc.server, xmlrpc.client, urllib.parse,cmd文档
https://docs.python.org/3/library/xmlrpc.server.html https://docs.python.org/3/library/xmlrpc.client.html https://docs.python.org/3/library/urllib.parse.html https://docs.python.org/3/library/cmd.html
运行节点

$: python simple_node.py http://localhost:4242
连接

>>> from xmlrpc.client import *

>>> one = ServerProxy('http:localhost:4242')

>>> one.query('test.txt') 调用服务器的query方法
"""

------------------------------------------------------------------------------------------------------------

#!/usr/bin/python

# *-*coding:utf-8 *-*

#pyhton3 新的Node实现(server.py)

from xmlrpc.client import ServerProxy, Fault, Binary

#from xmlrpclib import ServerProxy, Fault

from os.path import join, abspath, isfile

from xmlrpc.server import SimpleXMLRPCServer

#from SimpleXMLRPCServer import SimpleXMLRPCServer

from urllib.parse import urlparse

#from urlparse import urlparse

import sys

#SimpleXMLRPCServer.allow_reuse_address = 1 #源文件已经是True

MAX_HISTORY_LENGTH = 6

UNHANDLED = 100

ACCESS_DENIED = 200

class UnhandledQuery(Fault):
"""
表示无法处理的查询异常
"""
def __init__(self, message="Couldn't 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):
"""
检查给定的目录中是否有给定的文件名
"""
dir = abspath(dir)
name = abspath(name)
return name.startswith(join(dir, ''))

def getPort(url):
"""
从url中提取端口
"""
name = urlparse(url)[1]
parts = name.split(':')
return int(parts[-1])

class Node:
"""
p2p网络中的节点
"""
def __init__(self, url, dirname, secret):
self.url = url
self.dirname = dirname
self.secret = secret
self.known = set()

def query(self, query, history=[]):
"""
查询文件,可能会向其他已知节点请求帮助,将件作为字符串返回
"""
try:
return self._handle(query)

except UnhandledQuery: 
history = history + [self.url]
if len(history) >= MAX_HISTORY_LENGTH: raise
return self._broadcast(query, history)

def hello(self, other):
"""
用于将节点介绍给其他节点
当前节点使用hello将其他节点添加到self.known集合中,调用query-broadcast
使用其他节点打开下载文件
"""
self.known.add(other)
return 0

def fetch(self, query, secret):
"""
节点找到文件并且下载
open打开文件写入数据
"""
if secret != self.secret: raise AccessDenied
result = self.query(query)
#f = open(join(self.dirname, query), 'w')
#f.write(result)
#传输大的文件图片视频
f = open(join(self.dirname, query), 'wb')
f.write(result.data)
f.close()
return 0

def _start(self):
"""用于启动XML_RPC服务器, 将实例注册"""
s = SimpleXMLRPCServer(("", getPort(self.url)), logRequests=False)
s.register_instance(self)
s.serve_forever()

def _handle(self, query):
"""处理查询请求。 将找到的文件返回"""
dir = self.dirname
name = join(dir, query)
if not isfile(name): raise UnhandledQuery
if not inside(dir, name): raise AccessDenied
#使用Binary函数传输图片视频
return Binary(open(name, 'rb').read())
#return open(name).read()

def _broadcast(self, query, history):
"""
用于将查询广播到所有的已知节点.
也就是当前节点用helle方法将其他节点添加到self.known中
用其他节点创建client,然后返回查询值
"""
for other in self.known.copy():
if other in history: continue
try:
s = ServerProxy(other)
return s.query(query, history)

except Fault as f:
if f.faultCode == UNHANDLED: pass
else: self.known.remove(other)

#except Exception as e:
except:
self.known.remove(other)
raise UnhandledQuery

def main():
url, dirname, secret = sys.argv[1:]
n = Node(url, dirname, secret)
n._start()

if __name__ == '__main__':
main()

----------------------------------------------------------------------------------------------------

#!/usr/bin/python

# *-*coding:utf-8 *-*

#python3 新的Node控制器界面(client.py)

#from xmlrpclib import ServerProxy, Fault

from xmlrpc.client import ServerProxy, Fault

from cmd import Cmd

from random import choice

#from string import lowercase

from string import ascii_lowercase

from server import Node, UNHANDLED

from threading import Thread

from time import sleep

import sys

HEAD_START = 0.1#Seconds

SECRET_LENGTH = 100

def randomString(length):
"""
返回给定长度的由字母组成的随机字符串
"""
chars = []
#letters = lowercase[:26]
letters = ascii_lowercase[:26]
while length > 0:
length -= 1
chars.append(choice(letters))
return ''.join(chars)

class Client(Cmd):
"""
Node类的简单的基于文本的界面
"""
promt = '>'

def __init__(self, url, dirname, urlfile):
"""
设定url, dirname和urlfile,并且在单独的线程中启动Node服务器
"""
Cmd.__init__(self)
self.secret = randomString(SECRET_LENGTH)
n = Node(url, dirname, self.secret)
t = Thread(target=n._start)
t.setDaemon(1)
t.start()
#停0.1秒让服务器先启动
sleep(HEAD_START)
self.server = ServerProxy(url)
for line in open(urlfile, 'r'):
line = line.strip()
self.server.hello(line)

def do_fetch(self, arg):
"""调用服务器的fetch方法"""
try:
self.server.fetch(arg, self.secret)
except Fault as f:
if f.faultCode != UNHANDLED: raise
print("Couldn't find the file", arg, f.faultCode, UNHANDLED, f)

def do_exit(self, arg):
"""退出程序 打印空行处于美观考虑..."""
print()
sys.exit()

do_EOF = do_exit #EoF与'exit'同义

#获取命令输入,添加到client中

def main():
urlfile, directory, url = sys.argv[1:]
client = Client(url, directory, urlfile)
client.cmdloop()

if __name__ == '__main__':
main()

"""

命令行 $ python client.py ~/urls.txt ~/directory/  http://localhost:4242
$ python client.py ~/urls.txt ~/directory/  http://localhost:4243

urls.txt中每一行都包括程序所知道的其他节点的url地址

初始时4242端口的directory中没有要查询的文件。如果只运行4242命令行调用do_fetch,_broadcast的第二个except捕捉Connection refused错误。do_fetch返回Couldn'file,原因是4243的节点服务器还没有启动。

只运行4242不运行4243就会返回返回错误

"""
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  python xml_rpc