Distributed programming: Difference between revisions

Content added Content deleted
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, Plain Text===
===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
get = self.rfile.readline()
while True:
# write back to client
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__':
tcpserver = SocketServer.TCPServer((HOST, PORT), UpperCaseHandler)
rpcserver = RPCServer((HOST, PORT), RPCHandler)
rpcserver.object_to_proxy = MyHandlerInstance()
try:
try:
tcpserver.serve_forever()
rpcserver.serve_forever()
except KeyboardInterrupt:
except KeyboardInterrupt:
print 'Exiting...'
print 'Exiting...'
tcpserver.server_close()</lang>
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===