XML-RPC -- Python -- File copy from other Node

1. XML-RPC 文件复制

1.  Server 端

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

MAX_HISTORY_NODE_LENGTH = 6 #Node Length
UNHANDLED_CODE = 100 # Return Code
ACCESS_DENIED_CODE = 200 #Return Code

SimpleXMLRPCServer.allow_reuse_address = 1 #Port reuse
class UnhandledQuery ( Fault ):
'''
   That's show can't handle the query exception
    '''
def __init__ ( self , message = " Couldn't handle the query " ):
super (UnhandledQuery, self ).__init__ (UNHANDLED_CODE, message )

class AccessDenied ( Fault ):
'''
   When user try to access the forbiden resources raise exception
    '''
def __init__ ( self , message = " Access denied " ):
super (AccessDenied, self ).__init__ (ACCESS_DENIED_CODE, message )

def inside ( dir , name ):
'''
   Check the dir that user defined is contain the filename which the user given
    '''
dir = abspath ( dir )
    name = abspath (name )
return name.startswith (join ( dir, '' ))

def getPort ( url ):
'''
   Get the port from the url
    '''
    name = urlparse (url )[ 1 ] # '127.0.0.1:9090'
    port = int (name.split ( ' : ' )[ 1 ])
return port

class Node ( object ):
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_request (query )
except UnhandledQuery: #No file in local node
            history = history + [ self.url ]
if len (history ) > MAX_HISTORY_NODE_LENGTH:
raise
return self._boardcast (query, history )

def hello ( self , other ):
'''
       Hello to other nodes
        '''
self.known.add (other )
return 0

def fetch ( self , query , secret ):
'''
       find the file to download
        '''
if secret != self.secret:
raise AccessDenied
        result = self.query (query )
        f = open (join ( self.dirname, query ), ' w ' )
        f.write (result )
        f.close
return 0

def _start_rpc_server ( self ):
        s = SimpleXMLRPCServer (( '', getPort ( self.url )) )
        s.register_instance ( self )
        s.serve_forever ()

def _handle_request ( 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 _boardcast ( self , query , history ):
for other in self.known.copy ():
if other in history:
continue
try:
                s = ServerProxy (other )
return s.query (query, history ) # Other nodes to invoke the RPC SERVER Method
except Fault as f: #Fault init function has "faultCode and faultString"
if f.faultCode == UNHANDLED_CODE:
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_rpc_server ()

if __name__ == " __main__ ":
    main ()





Server端代码分析:

1. SimpleXMLRPCServer.allow_reuse_address = 1 表示所占用端口可以重用, 即使强制关掉Node Server之后再次重新启动, 也不会出现端口被占用的情况

2. UNHANDLED_CODE = 100    ACCESS_DENIED_CODE = 200 表示处理异常的两个返回码。

3. 过程: 启动远程调用服务器, 调用的接口就是Node类, Node类提供了三个可远程调用的方法, hello,fetch, query。 hello是添加新加入的结点信息到一个集合, fetch是用来获取数据的方法, query是查询遍历结点。

4. fetch方法,首先判断密码是否正确, 然后调用query查找, query的查找, 先是调用_handle_request 进行本地查找, 若没有找到,再通过_broadcast方法向自身维护的集合中结点发送广播消息, 这里会传入一个history, history是用来记录广播时, 不向已经发送广播的结点再次发送, history的长度是用来限制查询结点的长度。


Clinet端:

#/usr/bin/env python2.7


from cmd import Cmd
from xmlrpclib import ServerProxy, Fault
from random import choice
from string import lowercase
from server import Node, UNHANDLED_CODE
from threading import Thread
from time import sleep

import sys

HEAD_START = 0.1
SECRET_LENGTH = 100

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

class Client ( Cmd ):
    prompt = ' yzhao_terminal_cmd> '

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_rpc_server )
        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 as f:
if f.faultCode != UNHANDLED_CODE:
raise
print " Couldn't find the file ", arg

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

    do_EOF = do_exit

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

if __name__ == " __main__ ":
    main ()

Client端代码分析:

1. randomString 函数方法用来生成随机密码, Client类通过继承Cmd这个父类, 来实现交互式命令的输入; do_fetch就是调用server端的fetch方法获取资源数据, do_exit则是退出命令行,结束程序


Test

1. 分别创建两个文件夹test1 和 test2

2. test1中创建urltest1.txt, 内容为 http://127.0.0.1:3000

3. test2中创建urltest2.txt, 内容为 http://127.0.0.1:3001, 在test2中创建testfile.txt


4. 第一个控制台: python client.py test1/urltest1.txt  test1/  http://127.0.0.1:3001

    第二个控制台: python client.py test2/urltest2.txt  test2/  http://127.0.0.1:3000

在第一个控制台输入: fetch testfile.txt, 就可以将test2目录中的testfile.txt文件拷贝到test1目录中


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值