代码地址:https://code.csdn.net/ranky2009/pythonsmallproject
本项目基于Python基础教程(第二版)中的项目9:文件共享2。对代码略做修改,使得代码能在Win7系统,python3.4.3版本上运行。
原文中使用的是wxpython工具包做的界面,本处采用python中自带的tkinter制作简单的界面
代码如下:
1. simple_gui_client.py
import tkinter as tk
from client import Client
import sys
class Application(tk.Frame):
def __init__(self, master=None, client=None):
tk.Frame.__init__(self, master)
self.text = 'File Sharing Client'
self.pack()
self.createWidgets()
self.client = client
def createWidgets(self):
self.httpurlEntry = tk.Entry(self)
self.httpurlEntry.pack()
self.hi_there = tk.Button(self)
self.hi_there["text"] = "Fetch"
self.hi_there["command"] = self.say_hi
self.hi_there.pack()
#self.hi_there.pack(side="top")
self.QUIT = tk.Button(self, text="QUIT", fg="red",
command=self.master.destroy)
self.QUIT.pack(side="bottom")
def say_hi(self):
text = self.httpurlEntry.get()
print(text)
self.client.do_fetch(text)
def main():
urlfile, directory, url = sys.argv[1:]
if urlfile and directory and url:
client = Client(url, directory, urlfile)
root = tk.Tk()
app = Application(master=root, client=client)
app.mainloop()
if __name__ == '__main__': main()
2. client.py
from os import path
from random import choice
from string import ascii_lowercase
from server import Node, UNHANDLED
from threading import Thread
from time import sleep
from xmlrpc.client import ServerProxy, Fault
import sys
HEAD_START = 0.1 # Seconds
SECRET_LENGTH = 100
def randomString(length):
chars = []
letters = ascii_lowercase[:26]
while length > 0:
length -= 1
chars.append(choice(letters))
return ''.join(chars)
class Client():
def __init__(self, url, dirname, urlfile):
self.secret = randomString(SECRET_LENGTH)
n = Node(url, dirname, self.secret)
t = Thread(target=n._start)
t.setDaemon(1)
t.start()
sleep(HEAD_START)
self.server = ServerProxy(url)
urlfile = path.join(dirname, urlfile)
for line in open(urlfile):
line = line.strip()
self.server.hello(line)
def do_fetch(self, arg):
try:
self.server.fetch(arg, self.secret)
except Fault as f:
if f.faultCode != UNHANDLED: raise
print("Couldn't find the file", arg)
def main():
urlfile, directory, url = sys.argv[1:]
client = Client(url, directory, urlfile)
if __name__ == '__main__': main()
3. server.py
from os.path import join, isfile, abspath
from xmlrpc.client import Fault
from xmlrpc.server import SimpleXMLRPCServer
from xmlrpc.client import ServerProxy
from urllib.parse import urlparse
import sys
MAX_HISTORY_LENGTH = 6
UNHANDLED = 100
ACCESS_DENIED = 200
class UnhandleQuery(Fault):
def __init__(self, message="Couldn't handle the query"):
Fault.__init__(self, UNHANDLED, message)
class AccessDenied(Fault):
def __init__(self, message="Acces 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=[]):
"""
查询文件,可能会向其他已知节点请求帮助。讲文件作为字符串返回。
"""
print('in function query')
try:
content = self._handle(query)
except:
history = history + [self.url]
print('in funciton query, history : %s' % history)
if len(history) >= MAX_HISTORY_LENGTH: raise
content = self._broadcast(query, history)
return content
def hello(self, other):
"""
用于将节点介绍给其他节点。
"""
self.known.add(other)
return 0
def fetch(self, query, secret):
"""
用于让节点找到文件并且下载。
"""
#print(query)
#print(secret)
if secret != self.secret: raise AccessDenied
result = self.query(query)
#print(result)
f = open(join(self.dirname, query), 'w')
f.write(result)
f.close()
return 0
def _start(self):
"""
内部使用,用于启动XML_RPC服务器。
"""
s = SimpleXMLRPCServer(("", getPort(self.url)))
s.register_instance(self)
s.serve_forever()
def _handle(self, query):
"""
内部使用,用于处理请求。
"""
dir = self.dirname
name = join(dir, query)
print('in function _handle, file name : %s' % name)
if not isfile(name): raise UnhandleQuery
if not inside(dir, name): raise AccessDenied
return open(name).read()
def _broadcast(self, query, history):
for other in self.known.copy():
print('other: %s' % other)
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:
self.known.remove(other)
raise UnhandleQuery
def main():
url, directory, secret = sys.argv[1:]
n = Node(url, directory, secret)
n._start()
if __name__ == '__main__': main()
启动程序:
前期准备与project8类似。创建两个文件夹files1,files2,在这个两个文件夹中创建配置urlfile.txt,
files1中urlfile.txt的内容为http://localhost:4243,files2中的配置文件内容为空。
在files2文件夹中添加一个文件testfile.txt,用来下载。
启动一个server.在cmd中输入python server.py files2 12345
启动另外一个cmd,输入python simple_gui_client.py urlfile.txt files1 http://localhost:4242
在启动的界面中的文本框中输入testfile.txt,然后点击fetch按钮,就可以发现文件已经下载到文件夹files1中了了