def __init__(self, client_queue, input_queue): threading.Thread.__init__(self) self.client_queue = client_queue self.input_queue = input_queue # We'll store clients we receive from the queue here. self.clients = [] self.packet_builder = PacketBuilder()
class ClientThread(threading.Thread): """Implements a threaded consumer object which can handle multiple clients. This object maintains a listing of network sockets and receives data from a synchronized queue. After being notified by an input event, the object dequeues new data, formats it, and sends it out to registered sockets.""" def __init__(self, client_queue, input_queue): threading.Thread.__init__(self) self.client_queue = client_queue self.input_queue = input_queue # We'll store clients we receive from the queue here. self.clients = [] self.packet_builder = PacketBuilder() def run(self): while True: # Add any new clients to the queue. self.handle_new_clients() self.handle_new_data() def handle_new_clients(self): """Get a new client socket from the client queue and begin servicing it. """ while not self.client_queue.empty(): try: # We'll attempt to pull our new client out of the queue, but # only if we can do so without blocking. self.clients.append(self.client_queue.get(False)) except Queue.Empty: pass def handle_new_data(self): """Read, format, and distribute new data.""" # We'll try to read all of the available data at once. while not self.input_queue.empty(): try: # We're the only consumer thread, so we can safely remove data. byte = self.input_queue.get(None) self.packet_builder.add_byte(byte) if self.packet_builder.packet_complete: self.send_packet(self.packet_builder.packet) except Queue.Empty: # The data should have been in the queue, but if it isn't # it's no big deal - we haven't modified any object state. break def send_packet(self, packet): # Perhaps we had a formatting error (checksum fail?). Drop it. if not packet: return # Pass the packet to a formatting function to get it in the right form. packet = format(packet) # Note: We're making a copy of self.clients here. This is so # we can safely mutate self.clients without skipping elements. for client in self.clients[:]: try: client.send(packet) except socket.error, e: # Broken pipe, client has disconnected. Remove them # from our list and carry on. self.clients.remove(client) print "Client disconnected. %d remaining." % len(self.clients)