linux 下 rpc python 实例之使用XML-RPC进行远程文件共享

  这是个不错的练习,使用python开发P2P程序,或许通过这个我们可以自己搞出来一个P2P下载工具,类似于迅雷。XML-RPC是一个远程过程调用(remote procedure call,RPC)的分布式计算协议,通过XML将调用函数封装,并使用HTTP协议作为传送机制[摘自维基百科]

1.先做一个小小的尝试: 首先进入命令行,输入vim pythonServer.py,然后输入一下代码:

from simpleXMLRPCServerr import SimpleXMLRPCServerr
s = SimpleXMLRPCServer(("",4242))  #默认为本机
def twice(x):
return x*x

s.register_function(twice) #向服务器添加功能
s.serve_forever() #启动服务器

2. 输入python,打开shell 交互命令行

from xmlrpclib import ServerProxy
s = ServerProxy(‘http://localhost:4242‘)
s.twice(6) #通过ServerProxy调用远程的方法,

3.  开始文件共享完整代码:
3.1 服务器端Server.py

#coding=utf-8

from xmlrpclib import ServerProxy,Fault
from os.path import join, abspath,isfile
from SimpleXMLRPCServer import SimpleXMLRPCServer
from urlparse import urlparse
import sys

SimpleXMLRPCServer.allow_reuse_address = 1

MAX_HISTORY_LENGTH = 6

UNHANDLED = 100
ACCESS_DENIED = 200

class UnhandledQuery(Fault):
    ‘‘‘
    that‘s show can‘t handle the query exception
    ‘‘‘
    def __init__(self,message="Couldn‘t handle the query"):
        Fault.__init__(self, UNHANDLED, message)

class AccessDenied(Fault):
    ‘‘‘
    when user try to access the forbiden resources raise exception
    ‘‘‘
    def __init__(self, message="Access denied"):
        Fault.__init__(self, ACCESS_DENIED, message)

def inside(dir,name):
    ‘‘‘
    check the dir that user defined is contain the filename the user given
    ‘‘‘
    dir = abspath(dir)
    name = abspath(name)
    return name.startswith(join(dir,‘‘))
def getPort(url):
    ‘‘‘
    get the port num from the url
    ‘‘‘
    name = urlparse(url)[1]
    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 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):
        self.known.add(other)
        return 0
    def fetch(self, query, secret):

        if secret != self.secret: raise
        result = self.query(query)
        f = open(join(self.dirname, query),‘w‘)
        f.write(result)
        f.close()
        return 0

    def _start(self):
        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
        return open(name).read()

    def _broadcast(self, query, history):

        for other in self.known.copy():
            if other in history: continue
            try:
                s = ServerProxy(other)
                return s.query(query, history)
            except Fault, f:
                if f.faultCode == UNHANDLED:pass
                else: self.known.remove(other)
            except:
                self.known.remove(other)

        raise UnhandledQuery

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

if __name__ == ‘__main__‘:
    main()

  首先来看上面的几个常量设置: SimpleXMLRPCServer.allow_reuse_address表示,其所占用的端口可以重用,即如果你强制关闭node server之后再次重启,不会出现端口被占用的情况。

MAX_HISTORY_LENGTH = 6 这个是设置最大的节点长度,因为不能让让节点无休止的搜索下去。

UNHANDLED = 100 ACCESS_DENIED = 200 这俩就是返回码。

  然后再来看个node节点的具体流程。 这个段代码的流程这这样的,首先,启动供远程调用的服务器,调用的接口就是Node类。在Node类中有三个方法供远程调用的,一个是hello,一个是fetch还有一个query。hello 这个方法就是添加邻节点信息到当前节点中。而fetch则是用来获取数据的方法,query是节点之间用来交互的。

在fetch方法中,首先判断密码是否正确,然后通过调用自己的query方法查找数据。我们来看query方法,这个方法中,先是调用私有方法_handle本地查找,如果没找到,那么在通过_broadcast接口在所有已知节点中发送广播,这里要注意histroy,每次广播都会传递history这个参数,这个参数的作用有二:一是、防止往重复的节点中发送广播;二是、限制当前所有链接节点的长度。

理解了一个node server的基础功能之后,再来看对server进行管理的控制类代码。

3.2 客户端代码 Client.py

#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  #引入前面的server
from threading import Thread
from time import sleep

import sys

HEAD_START = 0.1
SECRET_LENGTH = 100

def randomString(length):
    chars = []
    letters = lowercase[:26]
    while length > 0:
        length -= 1
        chars.append(choice(letters))
    return ‘‘.join(chars)

class Client(Cmd):
    prompt = ‘> ‘

    def __init__(self, url, dirname, urlfile):

        Cmd.__init__(self)
        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)
        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,f:
            if f.faultCode != UNHANDLED: raise
            print "Couldn‘t find the file",arg

    def do_exit(self,arg):
        print
        sys.exit()

    do_EOR = do_exit

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

if __name__ == ‘__main__‘:
    main()

  来分析一下这段代码,前面的参数就不看了,很好理解,一开始有一个随机生成密码的函数,做什么用的呢?主要是用来防止别人非法调用该控制所控制的node server的。这密码 我们也不用记,因为我们有client的合法使用权。呵呵。

这段代码的总体作用就是为你提供一个可视的命令行的界面,通过继承cmd这个类,来解析你输入的命令,比如程序运行之后,出现命令提示符,你输入fetch,那么它会调用到do_fetch这个方法中来,并把参数传递进来。do_fetch这个方法的所用就是调用node server中的fetch方法,获取资源。另外的一个do_exit很好理解,就是接受exit命令退出程序。

  在程序初始化的时候,还有一点需要注意,就是它会读取你urlfile参数传递的文件中的数据,这个里面放的是节点的url地址。读取之后程序会把这些地址加到相邻节点中,供以后访问。不过这个程序还有些不完善的地方就是在程序运行时,如果你修改了url配置的文件,他不会读取你新添加的节点url。不过这个修改很简单,把获取url的代码放到do_fetch中就行了。

  在运行程序之前还有一些工作要做。 首先需要建立两个文件夹,A和C,C文件夹里面创建一个文件,B.txt,在A和C所在文件夹中建立urlsA.txt和urlsC.txt文件。里面在urlsA.txt中写入:http://localhost:4243,然后开启两个命令行,

第一个输入:

python client.py urlsA.txt A http://localhost:4242 

回车,是不是出来提示符了。输入fetch B.txt回车,看到提示Couldn‘t find the file B.txt。、

然后在第二个命令行中输入

python client.py urlsC.txt C http://localhost:4243

回车。同样输入fetch B.txt回车,是不是没反应。说明该文件存在。接在在第一个命令行中再次输入fetch B.txt看,是否还是提示没找到文件,如果你对代码根据我上面的建议进行了修改的话,就不会出现错误了,如果没有修改,此时你需要把输入exit退出程序,再次重启,然后在fetch B.txt,然后到A文件夹下查看一下,看是不是把B.txt下载到你的文件夹中了。

  PS:上面的程序只能传输文本文件,大文件或者其他格式的文件无法传输,刚才研究了一下,使用xmlrpclib这个库中的Binary函数即可,具体使用访问为: 先引入xmlrpclib,import xmlrpclib 在server类的的_handle方法中最后返回的那句代码return open(name).read() 修改为 return xmlrpclib.Binary(open(name,‘rb‘).read()) 再把fetch方法中的f.write(result)修改为f.write(result.data) 另外这句话前面的那个写文件的方式要改为wb。

【转自】 python项目练习八:使用XML-RPC进行远程文件共享 感谢楼主!

参考:

1 .python zeromq rpc介紹

2. 聊聊Python用rpc实现分布式系统调用的那些事

时间: 2024-08-24 15:17:15

linux 下 rpc python 实例之使用XML-RPC进行远程文件共享的相关文章

linux下的python基本介绍

[python]linux下的python安装及初步学习 linux下python的安装 尽管我的unbuntu 10.11版本已经默认更新了python的安装,但这里还是重新介绍一下如何在linux下安装   python.在网上也有一些安装教程. 摘来如下 1.下载源代码 http://www.python.org/ftp/python/2.5.2/Python-2.5.2.tar.bz2 2. 安装 $ tar –jxvf Python-2.5.2.tar.bz2 $ cd Python-

【转】Linux 下安装python软件包(pip、nose、virtualenv、distribute )

1.关于pip pip是python的软件安装工具,下面是pip的使用方法: (1)安装包:pip install SomePackage (2)查看安装包时安装了哪些文件:pip show --files SomePackage (3)查看哪些包有更新:pip show --files SomePackage (4)更新一个软件:pip install --upgrade SomePackage (5)卸载软件:pip uninstall SomePackage 所以,有了pip我们就可以自动

Linux下shell编程实例

1. 判断一文件是不是块或字符设备文件,如果是将其拷贝到 /dev 目录下 read -p "input a file:" filename if [ -b $filename -o -c $filename ] then cp $filename /dev/ fi 2.编写一个脚本,进行简单的减法运算,要求提示输入变量 #!/bin/bash read -p "input a number:" num1 read -p "input another nu

Linux 下安装python软件包(pip、nose、virtualenv、distribute )

新手刚开始学习Python,目前学习<笨方法学python>ing- 在学习习题46时需要安装几个软件包:pip.nose.virtualenv.distribute !在此记录Linux下安装python软件包的安装方法: 1.首先我们刚刚pip是什么? 在pip的python官网上我们可以看到,pip 是“A tool for installing and managing Python packages.”,也就是说pip是python的软件安装工具,下面是pip的使用方法: 安装包:

linux下更新Python版本并修改默认版本

linux下更新Python版本并修改默认版本,有需要的朋友可以参考下. 很多情况下拿到的服务器python版本很低,需要自己动手更改默认python版本 1.从官网下载python安装包(这个版本可以是任意版本3.3 2.7 2.6等等) wget http://python.org/ftp/python/2.7/Python-2.7.tar.bz2 2.解压并安装 tar -jxvf Python-2.7.tar.bz2 cd Python-3.3.0 ./configure make al

Linux下使用Python操作MySQL数据库

安装mysql-python 1.下载mysql-python 打开终端: cd /usr/local sudo wget http://nchc.dl.sourceforge.net/sourceforge/mysql-python/MySQL-python-1.2.2.tar.gz 官网地址:http://sourceforge.net/projects/mysql-python/ 2.解压 sudo tar -zxvf MySQL-python-1.2.2.tar.gz cd MySQL-

linux 下写python脚本实现自动补全( 我51论坛也有)

以前都是在windows下开发,但是对于玩linux的人来说,能够在linux下实现python脚本的自动补全的话,那是相当不错的,而我一般是使用的vim作为编辑器,且linux一般选择最小化安装,没有图形界面,参考了下网上的很多方法也没有适合自己,最后自己去看代码的帮助来实现了这个功能,废话不说,现在开始代码下载地址:http://vim.sourceforge.net/scripts/download_script.php?src_id=21842 文件名:pydiction-1.2.3.z

Redhat Linux下的python版本升级

执行#Python与#python -V,看到版本号是2.4.3,很老了,而且之前写的都是跑在python3.X上面的,3.X和2.X有很多不同, 有兴趣的朋友可以参考下这篇文章: http://www.jb51.net/article/34011.htm 更新python千万不要把老版本的删除!新老版本是可以共存的,很多基本的命令. 软件包都要依赖预装的老版本python的,比如yum. 一.升级到2.7.3 1. 升级安装 首先下载源tar包 可利用linux自带下载工具wget下载,如下所

Linux 下 新增Oracle10g 实例

主要分为5步:创建实例目录,创建密码文件,创建参数文件,创建建库脚本并建库,创建数据字典. 其中,需要特别注意2点: 目录的权限,即用户和所属用户组都要是oracle.可以切换到已存在的oracle用户或者以root创建,然后赋权. 创建实例的时候指定编码. 下面以 Oracle 10.2.0.1.0为例,开始创建: 说明: 此服务器上已经有一个正常运行的oracle实例cms,开机自启动,一个监听. 已有一个用户oracle,所属用户组oinstall. oracle环境变量:ORACLE_S

Linux下升级Python到3.5.2版本

原文出处:https://www.cnblogs.com/tssc/p/7762998.html 本文主要介绍在Linux(CentOS)下将Python的版本升级为3.5.2的方法 众所周知,在2020年python官方将不再支持2.7版本的python,所以使用3.x版本的python是必要的,但是linux中默认安装的python一般是2.6和2.7版本的,要是使用linux下的python就很有必要升级一下 进入正题 1.检查确认系统的相关信息 [[email protected] ~]