1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
|
'''
A "pickle" server and client implementation incapsulating request and reply implementations,
along with implementations suitable for sending mccode data graph and a simple string reply
combined with a server listdir() output.
This can be used to set up remote mccode data browsing with low requirements.
'''
import sys
import os
import socket
import pickle
from threading import Thread
sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..'))
from mccodelib.mcplotloader import McCodeDataLoader
'''
Protocol implementation.
'''
class RemoteCommand(object):
''' subclass and implement impl, which should return a RemoteReply instance '''
def handle(self, reply_sender):
''' reply_sender is a ReplyPickleSender '''
reply = self.impl()
reply_sender.send_reply(reply)
class RemoteReply(object):
''' subclass to implement server replies, NOTE: pickle.loads does not invoke __init__() '''
def __init__(self):
pass
class RCSimFile(RemoteCommand):
def __init__(self, simfile):
self.simfile = simfile
def impl(self):
loader = McCodeDataLoader(simfile=self.simfile)
loader.load()
graph = loader.plot_graph
reply = RRSimfile(graph)
return reply
def __str__(self):
return 'loading simfile: %s' % self.simfile
class RRSimfile(RemoteReply):
def __init__(self, plotgraph):
self.plotgraph = plotgraph
class RCList(RemoteCommand):
def impl(self):
lst = os.listdir('.')
text = '\n'.join(lst)
reply = RRList(text)
return reply
class RRList(RemoteReply):
def __init__(self, text):
self.text = text
'''
Client / server implementation.
'''
class McDataPickleClient(object):
''' receives, unpickles and dispatches RemoteCommand objects '''
def __init__(self, port):
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_address = ('localhost', port)
print('connecting to %s port %s' % server_address)
self.sock.connect(server_address)
self.sock.settimeout(0.5)
def ask(self, request):
try:
print('sending request: "%s"' % request)
text = pickle.dumps(request)
self.sock.sendall(text)
text = ''
while True:
try:
data = self.sock.recv(1024)
if data == '':
break
else:
text = text + data
except Exception as e:
print('timeout reached')
self.reply = pickle.loads(text)
finally:
self.sock.close()
class McDataPickleServer(object):
''' receives, unpickles and dispatches RemoteCommand objects '''
def __init__(self, port):
''' '''
self.port = port
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_name = 'localhost'
server_address = (server_name, self.port)
print('starting up on %s port %s' % server_address)
self.sock.bind(server_address)
self.sock.listen(1)
def listen(self):
''' execution loop '''
try:
while True:
print('waiting for a connection...')
connection, client_address = self.sock.accept()
print('client connected:', client_address)
text = ''
# NOTE: right now we assume, that we can get it all in one chunk with a blocking socket
data = connection.recv(1024)
text = text + data
# unpickle and dispatch
request = pickle.loads(text)
print('received request: "%s"' % request)
t = Thread()
rsend = ReplyPickleSender(connection)
t.run = lambda: request.handle(rsend)
t.start()
finally:
self.sock.close()
class ReplyPickleSender():
def __init__(self, connection):
self.connection = connection
def send_reply(self, reply):
''' callback for dispatched handlers - sends a reply though the socket '''
try:
s = pickle.dumps(reply)
print("sending data...")
self.connection.sendall(s)
finally:
self.connection.close()
|