P2P
peer to peer:即对等体到对等体;在传统的程序中,常常采用服务器-客户端模式的架构,客户端向服务器发送请求,服务器响应请求返回;P2P技术不同,这是一种远程过程(函数调用),不同于CS结构,p2p中每个程序对等,任何对等体可以连接到其他对等体
文件共享程序的需求
1.每个节点必须跟踪一组已知的结点,以便向其寻求帮助;同时还必须让结点能够向其他节点介绍自己,从而成为其他节点跟踪集中的一个
2.节点必须通过提供的文件名向其他节点请求文件;如果对方有这样的文件,应将其返回;没有向其他邻居请求改文件,请求的节点如果有此文件就返回
3.为避免循环请求,过长的请求链;向节点查询必须提供历史记录
4.必须能够连接到其他的结点,并将自己标识为信任方(密码);通过这样,结点可以使用如下载、存储等限制功能
5.用户界面
节点的实现
节点的属性:
目录名:让结点知道从哪里去查找文件或将文件存储在那
密码:
一组已知的对等体(URL)
URL:加入到查询历史记录中
node的方法:
查询:查询文件是否存在query()
获取和存储文件:fetch()
向其他节点介绍自己:hello()
节点的实现:
hello():hello()只需添加节点就可以了
fetch():
调用query()接收结果,如果查到了内容,将其存储在本地文件,没有返回fail
query():负责查询并返回结果,由于需要知道是否查到了数据,因此增加一个code作为返回码,为1时有值,为2时无值;
query()进一步抽象,设置_handle()和_broadcast()内部方法实现:
_handle()实现查询文件方法
_broadcast():向其他节点查询
simple_node.py
# --------------------------------------------------
# !/usr/bin/python
# -*- coding: utf-8 -*-
# PN: xml-rpc
# FN: simple_node
# Author: xiaxu
# DATA: 2022/4/22
# Description:初次实现简单的xml_rpc程序
# ---------------------------------------------------
from xmlrpc.client import ServerProxy
from xmlrpc.server import SimpleXMLRPCServer
from os.path import join,isfile
import sys
from urllib.parse import urlparse
OK = 1
Fail = 2
MAX_LENGTH = 6 #节点的最大长度为6
EMPTY = ''
def get_port(url):
"""
获取端口号
:param url:
:return:
"""
name = urlparse(url)[1] #获取ip:port
parts = name.split(":")
return int(parts[-1])
class Node:
def __init__(self,url,dirname,secret,):
self.url = url
self.dirname = dirname
self.secret = secret
self.known = set()
def hello(self,other):
"""
添加节点的url
:param other:
:return:
"""
self.known.add(other)
return OK
def query(self,query,history=[]):
"""
查询文件是否存在,向其他添加的节点查询;返回数据
:return:
"""
code,data = self._query(query)
if code ==OK:return code,data
else:
#如果没有查到就广播像其他节点查询
#print('向其他节点查询')
history = history+[self.url] #本url添加到查询记录
if len(history)>=MAX_LENGTH:
return Fail,EMPTY
return self._broadcast(query,history)
def fetch(self,query,secret):
"""
调用查询,存储文件
:return:
"""
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):
"""
启动方法
:return:
"""
s = SimpleXMLRPCServer(("",get_port(self.url)),logRequests=False)
s.register_instance(self)
s.serve_forever()
def _query(self,query):
"""
抽象的查询的方法
:return:
"""
dir = self.dirname
name = join(dir,query)
print("路径:",dir,name)
if not isfile(name):return Fail,EMPTY #Test whether a path is a regular file
return OK,open(name).read()
def _broadcast(self,query,history):
"""
向其他节点广播并查询
:return:
"""
for other in self.known.copy(): #因为这里的known()可能会被remove(),因此copy()
print("广播url:",other)
if other in history:continue
try:
s = ServerProxy(other)
code, data = s.query(query, history) #远程调用过程
if code == OK:
return code, data
except:
self.known.remove(other)
return Fail, EMPTY #如果其他节点没有查到就返回空
def main():
url, directory, secret = sys.argv[1:]
n = Node(url, directory, secret)
n._start()
if __name__ == '__main__':
main()