lizf2019 发表于 2022-4-16 17:21

p2p问题

本帖最后由 lizf2019 于 2022-4-16 18:52 编辑

想搞一个能p2p传文件的程序(有一台公网ip服务器,让不在同一局域网的A、B电脑互传文件)好像说服务器上装个什么nap就可以实现p2p
我想实现的p2p就是服务器建立A、B之间的连接,
文件传输的流量不过服务器

求助各位大佬:
1.服务器上需要部署什么什么程序(最好可以是win的,linux的也能接受)
2.想自己用C#写文件传输的客户端,有哪些实例推荐吗
另:syncthing这个软件是干什么用的呢,和p2p有关吗

atxz 发表于 2022-4-16 19:01

我也想问,期待有这样的大佬能给我们解答

bfgxp 发表于 2022-4-16 19:49

用现成的微力同步行不?
p2p模式分发

lizf2019 发表于 2022-4-16 20:11

bfgxp 发表于 2022-4-16 19:49
用现成的微力同步行不?
p2p模式分发

那个似乎不能二次开发{:1_924:}

hackerbob 发表于 2022-4-16 20:46

可以自己写p2p服务端

lizf2019 发表于 2022-4-16 21:18

hackerbob 发表于 2022-4-16 20:46
可以自己写p2p服务端

请问老哥有相关实例吗{:1_893:}

hackerbob 发表于 2022-4-16 21:27

本帖最后由 hackerbob 于 2022-4-16 21:29 编辑

lizf2019 发表于 2022-4-16 21:18
请问老哥有相关实例吗
从网上找的
服务端:
#-- coding:UTF-8 --
from xmlrpc.server import SimpleXMLRPCServer# 用于创建服务器
from xmlrpc.client import ServerProxy ,Fault # 用于向其它节点发出请求
from urllib.parse import urlparse# 用于URL解析
from os.path import join, isfile ,exists , abspath # 用于路径处理和文件查询
from os import mkdir
import sys

MAX_HISTORY_LENGTH = 6# 访问链最大长度

SimpleXMLRPCServer.allow_reuse_address = 1# 保证节点服务器重启时能够立即访问
UNHANDLED = 100# 文件不存在的异常代码
ACCESS_DENIED = 200# 文件访问受限的异常代码

class UnhandledQuery(Fault):# 创建自定义异常类
    def __init__(self, message='无法处理请求!'):# 定义构造方法
      Fault.__init__(self, UNHANDLED, message)# 重载超类构造方法

class AccessDenied(Fault):# 创建自定义异常类
    def __init__(self, message='访问资源受限!'):# 定义构造方法
      Fault.__init__(self, ACCESS_DENIED, message)# 重载超类构造方法

def inside(dir_path, file_path):# 定义文件路径检查的方法
    directory = abspath(dir_path)# 获取共享目录的绝对路径
    file = abspath(file_path)# 获取请求资源的绝对路径
    return file.startswith(join(directory, ''))# 返回请求资源的路径是否以共享目录路径开始

def get_port(url):# 定义获取端口号的函数
    result = urlparse(url)# 解析并获取URL中的[域名:端口号]
    port = result.split(':')[-1]# 获取以":"进行分割后的最后一组
    return int(port)# 转换为整数后返回

class Node:
    def __init__(self, url, dir_name, secret):
      self.url = url
      self.dirname = dir_name
      self.secret = secret
      self.known = set()

    def _start(self):# 定义启动服务器的方法
      server = SimpleXMLRPCServer(('', get_port(self.url)), logRequests=False)
      server.register_instance(self)# 注册类的实例到服务器对象
      server.serve_forever()

    def _handle(self, filename):# 定义处理请求的内部方法
      file_path = join(self.dirname, filename)# 获取请求路径
      if not isfile(file_path):# 如果路径不是一个文件
            # return FAIL, EMPTY# 返回无效状态和空数据
            raise UnhandledQuery# 抛出文件不存在的异常
      if not inside(self.dirname, file_path):# 如果请求的资源不是共享目录中的资源
            raise AccessDenied# 抛出访问资源受限异常
      return open(file_path).read()# 未发生异常时返回读取的文件数据
      # return OK, open(file_path).read()# 返回正常状态和读取的文件数据

    def _broadcast(self, filename, history):# 定义广播的内部方法
      for other in self.known.copy():
            if other in history:
                continue
            try:
                server = ServerProxy(other)
                return server.query(filename, history)
            except Fault as f:# 如果捕获访问故障异常获取异常代码
                if f.faultCode == UNHANDLED:# 如果是文件不存在异常
                  pass# 不做任何处理
                else:# 如果是其它故障异常
                  self.known.remove(other)# 从已知节点列表中移除节点
            except:# 如果捕获其它异常(非故障异常)
                self.known.remove(other)# 从已知节点列表中移除节点
      raise UnhandledQuery# >>如果已知节点都未能请求到资源<<,抛出文件不存在异常。   

    def query(self, filename, history=[]):# 定义接受请求的方法
      try:
            return self._handle(filename)
      except UnhandledQuery:# 如果捕获文件不存在的异常
            history.append(self.url)
            if len(history) >= MAX_HISTORY_LENGTH:
                raise
            return self._broadcast(filename, history)
      
    def hello(self, other):# 定义向添加其它节点到已知节点的方法
      self.known.add(other)# 添加其它节点到已知节点
      return OK# 返回值是必须的

    def fetch(self, filename, secret):# 定义下载的方法
      if secret != self.secret:# 如果密钥不匹配
            raise AccessDenied# 抛出访问资源受限异常
      result = self.query(filename)
      with open(join(self.dirname, filename), 'w') as file:
            file.write(result)
      return 0# 必须返回非None的值
      
def main():
    url, directory, secret = sys.argv
    node = Node(url, directory, secret)
    node._start()

if __name__ == '__main__':   
    main()

客户端:
from xmlrpc.client import ServerProxy, Fault# 导入服务器代{过}{滤}理类和故障类
from random import choice# 导入随机选取的方法
from string import ascii_lowercase# 导入小写字母列表对象
from time import sleep# 导入延迟方法
from server import Node, UNHANDLED# 导入服务器中的节类和变量
from threading import Thread# 导入线程类
from cmd import Cmd# 导入命令类
import sys# 导入系统模块
import os

HEAD_START = 0.1 # 等待服务器启动时长
SECRET_LENGTH = 10 # 密钥长度

def random_string(length):# 定义随机密钥的函数
    secret = ''
    while length > 0:
      length -= 1
      secret += choice(ascii_lowercase)# 随机获取小写字母叠加到变量
    return secret

# 因为要使用CMD界面,所以这个类要继承CMD类。
class Client(Cmd):
    prompt = '>>>'# 重写超类中的命令提示符

    def __init__(self, url_file, dir_name, url):# 定义构造方法
      Cmd.__init__(self)# 重载超类的构造方法
      self.secret = random_string(SECRET_LENGTH)# 创建密钥变量
      node = Node(url, dir_name, self.secret)# 创建节点对象
      thread = Thread(target=node._start)# 在独立的线程中启动服务器
      thread.setDaemon(True)# 将线程设置为守护线程
      thread.start()# 启动线程
      sleep(HEAD_START)# 等待服务器启动
      self.server = ServerProxy(url)# 创建服务器代{过}{滤}理对象
      for line in open(url_file):# 读取URL文件
            self.server.hello(line.strip())# 添加URL文件中的URL到已知节点集合

    # 类中对应命令的方法命名,都要以“do_”开头。
    def do_fetch(self, filename):# 定义下载命令的方法
      try:
            self.server.fetch(filename, self.secret)# 调用服务器代{过}{滤}理对象的下载方法
            print('调用服务器代{过}{滤}理对象的下载方法')
      except Fault as f:# 捕获故障异常
            if f.faultCode != UNHANDLED:# 如果异常代码不是未找到文件
                pass# 不做处理
            print('找不到文件:', filename)

    def do_exit(self, arg):# 定义退出命令的方法
      print('------------------退出程序------------------')
      sys.exit()# 系统退出

def main():# 定义主程序函数
    urlfile, dir_name, url = sys.argv# 获取通过命令行输入的参数
    client = Client(urlfile, dir_name, url)# 创建客户端对象
    client.cmdloop()# 启动命令行循环执行

if __name__ == '__main__':
    print('Download: >>>fetch ')
    print('Exit: >>>exit')
    main()
页: [1]
查看完整版本: p2p问题