Distributed programming: Difference between revisions
Content added Content deleted
Capra Hircus (talk | contribs) No edit summary |
(→Socket, Plain Text: changed the code to make it satisfy the task description) |
||
Line 974: | Line 974: | ||
print 'Server Message: %s' % response.read()</lang> |
print 'Server Message: %s' % response.read()</lang> |
||
===Socket, |
===Socket, Pickle format=== |
||
{{incomplete|Python}} |
|||
'''Protocol:''' Plain Text |
|||
'''Protocol:''' raw socket / pickle format |
|||
Use with Pythons [http://docs.python.org/library/pickle.html#module-pickle pickle module] for data serialization into printable text would allow the transfer of arbitrary Python data, but as it stands, this method is too low level to fulfill the task. |
|||
This example builds a very basic RPC mechanism on top of sockets and the [http://docs.python.org/library/pickle.html#module-pickle pickle module]. Please note that the pickle module is not secure - a malicious client can build malformed data to execute arbitrary code on the server. If untrusted clients can access the server, the [http://docs.python.org/library/json.html json module] could be used as a substitute, but we lose the ability to transfer arbitrary Python objects that way. |
|||
==== Server ==== |
==== Server ==== |
||
<lang python>#!/usr/bin/python |
<lang python>#!/usr/bin/python |
||
# -*- coding: utf-8 -*- |
# -*- coding: utf-8 -*- |
||
import SocketServer |
import SocketServer |
||
import pickle |
|||
HOST = "localhost" |
HOST = "localhost" |
||
PORT = 8000 |
PORT = 8000 |
||
class RPCServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer): |
|||
# our instance that will upper whatever it gets and send back to client |
|||
# The object_to_proxy member should be set to the object we want |
|||
class UpperCaseHandler(SocketServer.StreamRequestHandler): |
|||
# methods called on. Unfortunately, we can't do this in the constructor |
|||
# because the constructor should not be overridden in TCPServer... |
|||
daemon_threads = True |
|||
class RPCHandler(SocketServer.StreamRequestHandler): |
|||
def handle(self): |
def handle(self): |
||
in_channel = pickle.Unpickler(self.rfile) |
|||
print '%s connected' % self.client_address[0] |
|||
out_channel = pickle.Pickler(self.wfile, protocol=2) |
|||
# get what client sends |
|||
while True: |
|||
try: |
|||
name, args, kwargs = in_channel.load() |
|||
self.wfile.write(get.upper()) |
|||
print 'got %s %s %s' % (name, args, kwargs) |
|||
except EOFError: |
|||
# EOF means we're done with this request. |
|||
# Catching this exception to detect EOF is a bit hackish, |
|||
# but will work for a quick demo like this |
|||
break |
|||
try: |
|||
method = getattr(self.server.object_to_proxy, name) |
|||
result = method(*args, **kwargs) |
|||
except Exception, e: |
|||
out_channel.dump(('Error',e)) |
|||
else: |
|||
out_channel.dump(('OK',result)) |
|||
class MyHandlerInstance(object): |
|||
def echo(self, data): |
|||
'''Method for returning data got from client''' |
|||
return 'Server responded: %s' % data |
|||
def div(self, dividend, divisor): |
|||
'''Method to divide 2 numbers''' |
|||
return dividend/divisor |
|||
def is_computer_on(self): |
|||
return True |
|||
if __name__ == '__main__': |
if __name__ == '__main__': |
||
rpcserver = RPCServer((HOST, PORT), RPCHandler) |
|||
rpcserver.object_to_proxy = MyHandlerInstance() |
|||
try: |
try: |
||
rpcserver.serve_forever() |
|||
except KeyboardInterrupt: |
except KeyboardInterrupt: |
||
print 'Exiting...' |
print 'Exiting...' |
||
rpcserver.server_close() |
|||
</lang> |
|||
==== Client ==== |
==== Client ==== |
||
<lang python>#!/usr/bin/python |
<lang python>#!/usr/bin/python |
||
# -*- coding: utf-8 -*- |
# -*- coding: utf-8 -*- |
||
import socket |
import socket |
||
import pickle |
|||
HOST = "localhost" |
HOST = "localhost" |
||
PORT = 8000 |
PORT = 8000 |
||
class RPCClient(object): |
|||
DATA = "my name is eren" |
|||
def __init__(self, host, port): |
|||
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) |
|||
self.socket.connect((host, port)) |
|||
self.rfile = self.socket.makefile('rb') |
|||
self.wfile = self.socket.makefile('wb') |
|||
self.in_channel = pickle.Unpickler(self.rfile) |
|||
self.out_channel = pickle.Pickler(self.wfile, protocol=2) |
|||
def _close(self): |
|||
# connect to server and send data |
|||
self.socket.close() |
|||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) |
|||
self.rfile.close() |
|||
sock.connect((HOST, PORT)) |
|||
self.wfile.close() |
|||
sock.send("%s\n" % DATA) |
|||
# Make calling remote methods easy by overriding attribute access. |
|||
# get |
|||
# Accessing any attribute on our instances will give a proxy method that |
|||
response = sock.recv(256) |
|||
# calls the method with the same name on the remote machine. |
|||
sock.close() |
|||
def __getattr__(self, name): |
|||
def proxy(*args, **kwargs): |
|||
self.out_channel.dump((name, args, kwargs)) |
|||
self.wfile.flush() # to make sure the server won't wait forever |
|||
status, result = self.in_channel.load() |
|||
if status == 'OK': |
|||
return result |
|||
else: |
|||
raise result |
|||
return proxy |
|||
if __name__ == '__main__': |
|||
# connect to server and send data |
|||
rpcclient = RPCClient(HOST, PORT) |
|||
print 'Testing the echo() method:' |
|||
print "We sent: %s" % DATA |
|||
print rpcclient.echo('Hello world!') |
|||
print 'Server responded: %s' % response</lang> |
|||
print |
|||
print 'Calculating 42/2 on the remote machine:' |
|||
print rpcclient.div(42, 2) |
|||
print |
|||
print 'is_computer_on on the remote machine returns:' |
|||
print rpcclient.is_computer_on() |
|||
print |
|||
print 'Testing keyword args:' |
|||
print '42/2 is:', rpcclient.div(divisor=2, dividend=42) |
|||
rpcclient._close() |
|||
del rpcclient</lang> |
|||
===Pyro=== |
===Pyro=== |