2016-12-09 9 views
0

Python의 xmlrpc 모듈과 관련하여 Pickle-RPC를 구현했습니다.RPC를 통해 등록 정보에 액세스하는 방법

클라이언트 측에서 RPC 서버에 등록 된 인스턴스의 메서드를 호출 할 수 있습니다.

s = ServerProxy(('127.0.0.1', 49152), DatagramRequestSender) 
s.foo('bar') 

속성에 액세스하고 싶습니다.

s = ServerProxy(('127.0.0.1', 49152), DatagramRequestSender) 
s.foobar 

어떻게 구현해야합니까?

client.py

import pickle 
import socket 
from io import BytesIO 


class StreamRequestSender: 
    max_packet_size = 8192 
    address_family = socket.AF_INET 
    socket_type = socket.SOCK_STREAM 

    def send_request(self, address, request): 
     with socket.socket(self.address_family, self.socket_type) as client_socket: 
      with client_socket.makefile('wb') as wfile: 
       pickle.dump(request, wfile) 
      with client_socket.makefile('rb') as rfile: 
       response = pickle.load(rfile) 
       return response 


class DatagramRequestSender(StreamRequestSender): 
    socket_type = socket.SOCK_DGRAM 

    def send_request(self, address, request): 
     with socket.socket(self.address_family, self.socket_type) as client_socket: 
      with BytesIO() as wfile: 
       pickle.dump(request, wfile) 
       client_socket.sendto(wfile.getvalue(), address) 
      data = client_socket.recv(self.max_packet_size) 
      with BytesIO(data) as rfile: 
       response = pickle.load(rfile) 
       return response 


class ServerProxy: 
    def __init__(self, address, RequestSenderClass): 
     self.__address = address 
     self.__request_sender = RequestSenderClass() 

    def __send(self, method, args): 
     request = (method, args) 
     response = self.__request_sender.send_request(self.__address, request) 
     return response 

    def __getattr__(self, name): 
     return _Method(self.__send, name) 


class _Method: 
    def __init__(self, send, name): 
     self.__send = send 
     self.__name = name 

    def __getattr__(self, name): 
     return _Method(self.__send, "{}.{}".format(self.__name, name)) 

    def __call__(self, *args): 
     return self.__send(self.__name, args) 


if __name__ == '__main__': 
    s = ServerProxy(('127.0.0.1', 49152), DatagramRequestSender) 
    print(s.pow(2, 160)) 

server.py

import pickle 
import sys 
from socketserver import StreamRequestHandler, DatagramRequestHandler, ThreadingTCPServer, ThreadingUDPServer 


class RPCRequestHandler: 
    def handle(self): 
     method, args = pickle.load(self.rfile) 
     response = self.server.dispatch(method, args) 
     pickle.dump(response, self.wfile) 


class StreamRPCRequestHandler(RPCRequestHandler, StreamRequestHandler): 
    pass 


class DatagramRPCRequestHandler(RPCRequestHandler, DatagramRequestHandler): 
    pass 


class RPCDispatcher: 
    def __init__(self, instance=None): 
     self.__instance = instance 

    def register_instance(self, instance): 
     self.__instance = instance 

    def dispatch(self, method, args): 
     _method = None 
     if self.__instance is not None: 
      try: 
       _method = self._resolve_dotted_attribute(self.__instance, method) 
      except AttributeError: 
       pass 
     if _method is not None: 
      return _method(*args) 
     else: 
      raise Exception('method "{}" is not supported'.format(method)) 

    @staticmethod 
    def _resolve_dotted_attribute(obj, dotted_attribute): 
     attributes = dotted_attribute.split('.') 
     for attribute in attributes: 
      if attribute.startswith('_'): 
       raise AttributeError('attempt to access private attribute "{}"'.format(attribute)) 
      else: 
       obj = getattr(obj, attribute) 
     return obj 


class RPCServer(ThreadingUDPServer, RPCDispatcher): 
    def __init__(self, server_address, RPCRequestHandlerClass, instance=None): 
     ThreadingUDPServer.__init__(self, server_address, RPCRequestHandlerClass) 
     RPCDispatcher.__init__(self, instance) 


if __name__ == '__main__': 
    class ExampleService: 
     def pow(self, base, exp): 
      return base ** exp 


    s = RPCServer(('127.0.0.1', 49152), DatagramRPCRequestHandler, ExampleService()) 
    print('Serving Pickle-RPC on localhost port 49152') 
    print('It is advisable to run this example server within a secure, closed network.') 
    try: 
     s.serve_forever() 
    except KeyboardInterrupt: 
     print("\nKeyboard interrupt received, exiting.") 
     s.server_close() 
     sys.exit(0) 

답변

1

당신은 그것을 것을 파악, 그 __getattr__ 메소드를 오버라이드를 통해 개체의 속성 액세스를 차단해야합니다 메소드 호출 대신 속성 액세스 일 뿐이며 메서드를 호출하지 않고 대신 참조 된 attr의 값을 반환하는 특정 메시지를 서버에 보냅니다. 직접 ibute. 정확히 what Pyro4 does입니다. 코드 : https://github.com/irmen/Pyro4/blob/master/src/Pyro4/core.py#L255

또한 : 피클을 직렬화 형식으로 사용하는 것은 엄청난 보안 문제입니다. 그것은 임의의 원격 코드 실행을 허용합니다. 그러므로 거룩한 모든 것을 사랑하기 때문에 고객을 절대적으로 신뢰하고 인터넷을 통한 임의 접속을 절대 허용하지 마십시오.

+0

Pyro4의'Proxy' 클래스는 올바르게 이해하면 동적으로 관리되는'_pyroAttrs'를 사용하여 원격 속성을받습니다. 우리는 의사 소통없이 동적으로 방법/속성을 단호히 해결할 수는 없습니까? 추신 Pyro4는 아주 멋진 라이브러리입니다. 나는 무엇인가 묻고 싶다. 그 중 하나 인 Pyro 4는 TCP와 UDP의 차이를 어떻게 흡수합니까? 고맙습니다. – sira

+0

질문을 이해할 수 없습니다. Pyro4는 TCP/IP 소켓을 독점적으로 사용하기 때문에, Pyro가 UDP를 통해 신뢰할 수있는 RPC를 수행 할 수있는 방법은 없습니다 (이는 패킷의 순서를 보장하지 못하는 신뢰할 수없는 프로토콜입니다). 이 문제를 논의 할 곳 : –

+0

아, 알겠습니다. 고맙습니다. – sira