Welcome, guest | Sign In | My Account | Store | Cart

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.

Python, 131 lines
  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.