import socket, select import os.path, mimetypes class Server(object): """ Asynchronous server class. specify a client handler class and port in the constructor. """ def __init__(self, connection_class, port=8080): self.port = port self.connection_class = connection_class self.listening_socket = socket.socket() self.listening_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.listening_socket.bind(('127.0.0.1', port)) self.listening_socket.listen(5) self.listening_socket.setblocking(0) self.handlers = {self.listening_socket: self} self.clients = [] def run(self): """ run the event loop - collect sockets and min timeout - do the select call and notify clients """ reads = [self.listening_socket] + [c.sock for c in self.clients] writes = [c.sock for c in self.clients if c.should_write()] others = [c.sock for c in self.clients] try: timeout = min([c.next_timeout() for c in self.clients]) except ValueError: timeout = None [rlist, wlist, xlist] = select.select(reads, writes, others, timeout) for readable in rlist: self.handlers[readable].do_read() for writable in wlist: self.handlers[writable].do_write() for otherable in xlist: self.handlers[otherable].do_other() for c in self.clients: c.try_timeout() def do_read(self): """ do_read method on the server itself (it's also in the select). accepts a connection. """ (sock, addrinfo) = self.listening_socket.accept() c = self.connection_class(self, sock, addrinfo) self.clients.append(c) self.handlers[sock] = c def client_closed(self, c): """ remove a client from the list and handlers """ self.clients.remove(c) del self.handlers[c.sock] # print "%d open connections" % len(self.clients) def close(self): """ close the server down and stop listening """ self.listening_socket.close() class Client(object): """Client is the generic Server handler class. look at HTTPClient for more documentation""" def __init__(self, parent, sock, addrinfo): self.parent = parent self.sock = sock self.sock.setblocking(0) self.addrinfo = addrinfo self.write_buf = "" self.read_buf = "" self.to_close = False #self.write("test") # self.close() def should_write(self): return len(self.write_buf) > 0 or self.to_close def do_read(self): """called by the event loop when the socket is readable""" data = self.sock.recv(4096) if not data: return self.close() self.read_buf += data self.on_data() def do_write(self): """called by the event loop when the socket is writable""" try: if self.write_buf: no_written = self.sock.send(self.write_buf) self.write_buf = self.write_buf[no_written:] if len(self.write_buf) == 0 and self.to_close: self.sock.close() self.parent.client_closed(self) except socket.error: self.on_error() def on_data(self): raise NotImplementedError() def do_other(self): pass def on_error(self): self.parent.client_closed(self) def try_timeout(self): pass def next_timeout(self): return None def write(self, msg): self.write_buf += msg def close(self): self.to_close = True class EchoClient(Client): """EchoClient is an example Client handler that implements an echo server""" def __init__(self, parent, sock, addrinfo): super(EchoClient, self).__init__(parent, sock, addrinfo) def on_data(self): self.write(self.read_buf) self.read_buf = ""