|
Description:
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.
Source: Text Source
'''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()
Discussion:
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.
|