This recipe allows the creation of servers that provide services to connecting programs. This is basically a remote procedure call (RPC) server that allows various clients to connect and communicate through registered functions.
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 | '''Module for networked function calls.
This module provides two server classes and a single client
class that can be used for function calls across a network.'''
__version__ = 1.0
################################################################################
import cPickle
import Queue
import socket
import thread
import z_string
import z_sync
class Protocol:
'Protocol() -> new Protocol object'
SEND_SIZE = 1024
RECV_SIZE = 4096
def send(self, sock, obj):
'Sends one Python object over a socket.'
self.sendall(sock, '\x00'.join([z_string.string_code(string) for string in z_string.partition(cPickle.dumps(obj, -1), self.SEND_SIZE)]))
def recv(self, sock):
'Receives one Python object over a socket.'
return cPickle.loads(''.join([z_string.code_string(code) for code in self.recvall(sock).split('\x00')]))
def sendall(self, sock, string):
'Sends an entire string over a socket.'
sock.sendall(string)
sock.shutdown(socket.SHUT_WR)
def recvall(self, sock):
'Receives an entire string over a socket.'
string = str()
while True:
buf = sock.recv(self.RECV_SIZE)
if buf:
string += buf
else:
sock.shutdown(socket.SHUT_RD)
return string
################################################################################
class Client(Protocol):
'Client(host, port) -> new Client'
def __init__(self, host, port):
'x.__init__(...) initializes x'
self.address = host, port
def __call__(self, name, *args, **kwargs):
'Allows a client object to be called as a function.'
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(self.address)
self.send(sock, (name, args, kwargs))
return self.recv(sock)
class Server(Protocol):
'Server(host, port) -> new Server'
def __init__(self, host, port):
'x.__init__(...) initializes x'
self.services = dict()
thread.start_new_thread(self.server, (host, port))
def server(self, host, port):
'Acts as a server for receiving connections.'
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind((host, port))
sock.listen(5)
while True:
thread.start_new_thread(self.client, (sock.accept()[0],))
def client(self, sock):
'Takes care of incoming connections.'
name, args, kwargs = self.recv(sock)
try:
self.send(sock, self.services[name](*args, **kwargs))
except:
self.send(sock, None)
def add_service(self, name, service):
'Adds a new function that the server can execute.'
self.services[name] = service
class Sync_Server(Server):
'Sync_Server(host, port) -> new Sync_Server'
def __init__(self, host, port):
'x.__init__(...) initializes x'
self.services = dict()
self.queue = Queue.Queue()
thread.start_new_thread(self.server, (host, port))
thread.start_new_thread(self.processor, ())
def client(self, sock):
'Takes care of incoming connections.'
name, args, kwargs = self.recv(sock)
sync = z_sync.Sync(2)
item = [name, args, kwargs, sync]
self.queue.put(item)
sync.sync()
self.send(sock, item[4])
def processor(self):
'Processes clients\' requests in sequential order.'
while True:
item = self.queue.get()
name, args, kwargs, sync = item
try:
item.append(self.services[name](*args, **kwargs))
except:
item.append(None)
sync.sync()
################################################################################
if __name__ == '__main__':
import sys
print 'Content-Type: text/plain'
print
print file(sys.argv[0]).read()
|
This module was used to create the programs listed in the ZChat recipe. Example code and usage is shown there and demonstrates a possible use of the z_service module. A test was conducted with Server, Output, and Input by people in the PST, CST, and EST zones; and the programs were confirmed to work well during the three-way conversation.