def __init__(self, certificatesDirectory=None): """ Initializes the NetworkManager's state. Args: certificatesDirectory: the directory where the files server.crt and server.key are. """ self.__connectionPool = GenericThreadSafeDictionary() self.__outgoingDataQueue = GenericThreadSafePriorityQueue() if (not reactor.running): self.__networkThread = TwistedReactorThread() else: self.__networkThread = None self.__outgoingDataThread = OutgoingDataThread( self.__outgoingDataQueue) self.__connectionThread = ConnectionMonitoringThread( self.__connectionPool) self.__certificatesDirectory = certificatesDirectory
def __init__(self, certificatesDirectory = None): """ Initializes the NetworkManager's state. Args: certificatesDirectory: the directory where the files server.crt and server.key are. """ self.__connectionPool = GenericThreadSafeDictionary() self.__outgoingDataQueue = GenericThreadSafePriorityQueue() if (not reactor.running) : self.__networkThread = TwistedReactorThread() else : self.__networkThread = None self.__outgoingDataThread = OutgoingDataThread(self.__outgoingDataQueue) self.__connectionThread = ConnectionMonitoringThread(self.__connectionPool) self.__certificatesDirectory = certificatesDirectory
def __allocateConnectionResources(self, callbackObject): """ Allocates the resources associated to a new connection (only if necessary) Args: callbackObject: The object that will process all the incoming packages. Returns: a tuple (queue, thread), where queue and thread are the incoming data queue and the incoming data processing thread assigned to this new connection. """ c = None for connection in self.__connectionPool.values(): if (connection.getCallback() == callbackObject): # Everything is allocated. Let's reuse it. c = connection break if c != None: return (c.getQueue(), c.getThread()) else: queue = GenericThreadSafePriorityQueue() thread = IncomingDataThread(queue, callbackObject) return (queue, thread)
class NetworkManager(): """ This class provides a facade to use Twisted at a higher abstraction level. @attention: If you don't want everything to conk out, DO NOT USE MORE THAN ONE NetworkManager IN THE SAME PROGRAM. @attention: Due to some Twisted related limitations, do NOT stop the network service UNTIL you KNOW PERFECTLY WELL that you won't be using it again. """ def __init__(self, certificatesDirectory=None): """ Initializes the NetworkManager's state. Args: certificatesDirectory: the directory where the files server.crt and server.key are. """ self.__connectionPool = GenericThreadSafeDictionary() self.__outgoingDataQueue = GenericThreadSafePriorityQueue() if (not reactor.running): self.__networkThread = TwistedReactorThread() else: self.__networkThread = None self.__outgoingDataThread = OutgoingDataThread( self.__outgoingDataQueue) self.__connectionThread = ConnectionMonitoringThread( self.__connectionPool) self.__certificatesDirectory = certificatesDirectory def startNetworkService(self): """ Starts the network service. When this operation finishes, new connections can be established. Args: None Returns: Nothing """ if (self.__networkThread != None): self.__networkThread.start() self.__outgoingDataThread.start() self.__connectionThread.start() def stopNetworkService(self): """ Stops the network service. @attention: Due to Twisted related limitations, do NOT stop the network service UNTIL you KNOW PERFECTLY WELL that you won't be using it again. @attention: Remember: there's just one network manager per application, so *please* THINK before you stop it. @attention: If this method is called from a network thread, your application will HANG. Please, call it from your application's MAIN thread. Args: None Returns: Nothing """ for connection in self.__connectionPool.values(): self.closeConnection(connection.getHost(), connection.getPort()) self.__connectionThread.stop() self.__connectionThread.join() self.__outgoingDataThread.stop() self.__outgoingDataThread.join() if (self.__networkThread != None): self.__networkThread.stop() self.__networkThread.join() def __allocateConnectionResources(self, callbackObject): """ Allocates the resources associated to a new connection (only if necessary) Args: callbackObject: The object that will process all the incoming packages. Returns: a tuple (queue, thread), where queue and thread are the incoming data queue and the incoming data processing thread assigned to this new connection. """ c = None for connection in self.__connectionPool.values(): if (connection.getCallback() == callbackObject): # Everything is allocated. Let's reuse it. c = connection break if c != None: return (c.getQueue(), c.getThread()) else: queue = GenericThreadSafePriorityQueue() thread = IncomingDataThread(queue, callbackObject) return (queue, thread) def connectTo(self, host, port, timeout, callbackObject, useSSL=True, reconnect=False): """ Connects to a remote server using its arguments @attention: This is a blocking operation. The calling thread will be blocked until the connection is established or until a timeout error is detected. Args: host: host IP address port: the port where the host is listenning timeout: timeout in seconds. callbackObject: the callback object that will process all the incoming packages received through this connection. useSSL: if True, the SSL version 4 protocol will be used. Otherwise, we'll stick to TCP version 4. reconnect: if True, the network subsystem will try to reconnect to the server when the connection is lost. Returns: Nothing Raises: NetworkManagerException: If no answer is received after timeout seconds, the connection process will be aborted and a NetworkManagerException will be raised. """ if self.__connectionPool.has_key((host, port)): raise NetworkManagerException("The port " + str(port) + " is already in use") # The port is free => proceed # Allocate the connection resources (queue, thread) = self.__allocateConnectionResources(callbackObject) # Create the NetworkConnection object connection = ClientConnection(useSSL, self.__certificatesDirectory, host, port, queue, thread, reconnect, callbackObject) # Try to establish the connection try: if (connection.establish(timeout)): # The connection could be created => add it to the connection pool self.__connectionPool[(host, port)] = connection else: # Raise an exception raise NetworkManagerException(str(connection.getError())) except ConnectionException as e: raise NetworkManagerException(e.message) def listenIn(self, port, callbackObject, useSSL=False): """ Creates a server using its arguments. @attention: This is a non-blocking operation. Please, check if the connection is ready BEFORE you send anything through it. Args: port: The port to listen in. If it's not free, a NetworkManagerException will be raised. callbackObject: the callback object that will process all the incoming packages received through this connection. useSSL: if True, the SSL version 4 protocol will be used. Otherwise, we'll stick to TCP version 4. Returns: Nothing """ if self.__connectionPool.has_key(('', port)): raise NetworkManagerException("The port " + str(port) + " is already in use") # The port is free => proceed # Allocate the connection resources (queue, thread) = self.__allocateConnectionResources(callbackObject) # Create the NetworkConnection object connection = ServerConnection(useSSL, self.__certificatesDirectory, port, queue, thread, callbackObject) try: # Establish it connection.establish(None) if (connection.getError() == None): # Register the new connection self.__connectionPool[('', port)] = connection else: raise ConnectionException(str(connection.getError())) except ConnectionException as e: raise NetworkManagerException(e.message) def isConnectionReady(self, host, port): """ Checks wether a connection is ready or not. Args: port: the port assigned to the connection. Returns: True if the connection is ready or False otherwise. Raises: NetworkManagerException: a NetworkManagerException will be raised if - there were errors while establishing the connection, or if - the connection was abnormally closed, or if - the supplied port is free """ if not self.__connectionPool.has_key((host, port)): raise NetworkManagerException("The port " + str(port) + " is not in use") connection = self.__connectionPool[(host, port)] self.__detectConnectionErrors(connection) return connection.isReady() def __detectConnectionErrors(self, connection): """ Checks if the given connection is in an error state or was unexpectedly closed. Args: connection: the connection to monitor. Returns: Nothing """ if connection.isInErrorState(): # Something bad has happened => close the connection, warn the user self.__connectionPool.pop( (connection.getHost(), connection.getPort())) raise NetworkManagerException(str(connection.getError())) if (connection.wasUnexpectedlyClosed()): self.__connectionPool.pop( (connection.getHost(), connection.getPort())) raise NetworkManagerException( "The connection was closed abnormally") def sendPacket(self, host, port, packet, client_IP=None, client_port=None): """ Sends a packet through the specified port and IP address. Args: port: The port assigned to the connection that will be used to send the packet. packet: The packet to send. client_IP: the client's ipv4 address client_port: the client's port @attention: The last two parameters can only be used in server connections. In this case, - if both are None, the packet will be sent to all the clients (MULTICAST mode) - otherwise, the pacekt will be sent to a specific client (UNICAST mode) Returns: None if the packet was successfully sent and an error message if it wasn't. Raises: Nothing @attention: If the connection is not ready, the packet will be discarded. So PLEASE, check the connection's status BEFORE using it. """ if not self.__connectionPool.has_key((host, port)): return "There's nothing attached to the port " + str(port) connection = self.__connectionPool[(host, port)] if connection.isReady(): connection.registerPacket() self.__outgoingDataQueue.queue( packet.getPriority(), (connection, packet, client_IP, client_port)) return None else: return "The connection is not ready yet" def createPacket(self, priority): """ Creates an empty data packet and returns it Args: priority: The new packet's priority. Returns: a new data packet. Raises: NetworkManagerException: this exception will be raised when the packet's priority is not a positive integer. """ if not isinstance(priority, int) or priority < 0: raise NetworkManagerException( "Data packets\' priorities MUST be positive integers") p = _Packet(Packet_TYPE.DATA, priority) return p def closeConnection(self, host, port): """ Closes a connection Args: port: The port assigned to the connection. If it's free, a NetworkManagerException will be raised. Returns: Nothing """ if not self.__connectionPool.has_key((host, port)): raise NetworkManagerException( "There's nothing attached to the port " + str(port)) # Retrieve the connection connection = self.__connectionPool[(host, port)] # Ask the connection to close connection.close() @staticmethod def printConnectionWarningIfNecessary(ip, port, packet_type, errorMessage): if (errorMessage != None): print "Warning: unable to send {2} to {0}:{1}.".format( ip, port, packet_type) print "\t" + errorMessage
class NetworkManager(): """ This class provides a facade to use Twisted at a higher abstraction level. @attention: If you don't want everything to conk out, DO NOT USE MORE THAN ONE NetworkManager IN THE SAME PROGRAM. @attention: Due to some Twisted related limitations, do NOT stop the network service UNTIL you KNOW PERFECTLY WELL that you won't be using it again. """ def __init__(self, certificatesDirectory = None): """ Initializes the NetworkManager's state. Args: certificatesDirectory: the directory where the files server.crt and server.key are. """ self.__connectionPool = GenericThreadSafeDictionary() self.__outgoingDataQueue = GenericThreadSafePriorityQueue() if (not reactor.running) : self.__networkThread = TwistedReactorThread() else : self.__networkThread = None self.__outgoingDataThread = OutgoingDataThread(self.__outgoingDataQueue) self.__connectionThread = ConnectionMonitoringThread(self.__connectionPool) self.__certificatesDirectory = certificatesDirectory def startNetworkService(self): """ Starts the network service. When this operation finishes, new connections can be established. Args: None Returns: Nothing """ if (self.__networkThread != None) : self.__networkThread.start() self.__outgoingDataThread.start() self.__connectionThread.start() def stopNetworkService(self): """ Stops the network service. @attention: Due to Twisted related limitations, do NOT stop the network service UNTIL you KNOW PERFECTLY WELL that you won't be using it again. @attention: Remember: there's just one network manager per application, so *please* THINK before you stop it. @attention: If this method is called from a network thread, your application will HANG. Please, call it from your application's MAIN thread. Args: None Returns: Nothing """ for connection in self.__connectionPool.values() : self.closeConnection(connection.getHost(), connection.getPort()) self.__connectionThread.stop() self.__connectionThread.join() self.__outgoingDataThread.stop() self.__outgoingDataThread.join() if (self.__networkThread != None) : self.__networkThread.stop() self.__networkThread.join() def __allocateConnectionResources(self, callbackObject): """ Allocates the resources associated to a new connection (only if necessary) Args: callbackObject: The object that will process all the incoming packages. Returns: a tuple (queue, thread), where queue and thread are the incoming data queue and the incoming data processing thread assigned to this new connection. """ c = None for connection in self.__connectionPool.values() : if (connection.getCallback() == callbackObject) : # Everything is allocated. Let's reuse it. c = connection break if c != None : return (c.getQueue(), c.getThread()) else : queue = GenericThreadSafePriorityQueue() thread = IncomingDataThread(queue, callbackObject) return (queue, thread) def connectTo(self, host, port, timeout, callbackObject, useSSL=True, reconnect=False): """ Connects to a remote server using its arguments @attention: This is a blocking operation. The calling thread will be blocked until the connection is established or until a timeout error is detected. Args: host: host IP address port: the port where the host is listenning timeout: timeout in seconds. callbackObject: the callback object that will process all the incoming packages received through this connection. useSSL: if True, the SSL version 4 protocol will be used. Otherwise, we'll stick to TCP version 4. reconnect: if True, the network subsystem will try to reconnect to the server when the connection is lost. Returns: Nothing Raises: NetworkManagerException: If no answer is received after timeout seconds, the connection process will be aborted and a NetworkManagerException will be raised. """ if self.__connectionPool.has_key((host,port)) : raise NetworkManagerException("The port " + str(port) +" is already in use") # The port is free => proceed # Allocate the connection resources (queue, thread) = self.__allocateConnectionResources(callbackObject) # Create the NetworkConnection object connection = ClientConnection(useSSL, self.__certificatesDirectory, host, port, queue, thread, reconnect, callbackObject) # Try to establish the connection try : if (connection.establish(timeout)) : # The connection could be created => add it to the connection pool self.__connectionPool[(host, port)] = connection else : # Raise an exception raise NetworkManagerException(str(connection.getError())) except ConnectionException as e: raise NetworkManagerException(e.message) def listenIn(self, port, callbackObject, useSSL=False): """ Creates a server using its arguments. @attention: This is a non-blocking operation. Please, check if the connection is ready BEFORE you send anything through it. Args: port: The port to listen in. If it's not free, a NetworkManagerException will be raised. callbackObject: the callback object that will process all the incoming packages received through this connection. useSSL: if True, the SSL version 4 protocol will be used. Otherwise, we'll stick to TCP version 4. Returns: Nothing """ if self.__connectionPool.has_key(('', port)) : raise NetworkManagerException("The port " + str(port) +" is already in use") # The port is free => proceed # Allocate the connection resources (queue, thread) = self.__allocateConnectionResources(callbackObject) # Create the NetworkConnection object connection = ServerConnection(useSSL, self.__certificatesDirectory, port, queue, thread, callbackObject) try : # Establish it connection.establish(None) if (connection.getError() == None) : # Register the new connection self.__connectionPool[('', port)] = connection else : raise ConnectionException(str(connection.getError())) except ConnectionException as e: raise NetworkManagerException(e.message) def isConnectionReady(self, host, port): """ Checks wether a connection is ready or not. Args: port: the port assigned to the connection. Returns: True if the connection is ready or False otherwise. Raises: NetworkManagerException: a NetworkManagerException will be raised if - there were errors while establishing the connection, or if - the connection was abnormally closed, or if - the supplied port is free """ if not self.__connectionPool.has_key((host,port)) : raise NetworkManagerException("The port " + str(port) +" is not in use") connection = self.__connectionPool[(host, port)] self.__detectConnectionErrors(connection) return connection.isReady() def __detectConnectionErrors(self, connection): """ Checks if the given connection is in an error state or was unexpectedly closed. Args: connection: the connection to monitor. Returns: Nothing """ if connection.isInErrorState() : # Something bad has happened => close the connection, warn the user self.__connectionPool.pop((connection.getHost(), connection.getPort())) raise NetworkManagerException(str(connection.getError())) if (connection.wasUnexpectedlyClosed()): self.__connectionPool.pop((connection.getHost(), connection.getPort())) raise NetworkManagerException("The connection was closed abnormally") def sendPacket(self, host, port, packet, client_IP = None, client_port = None): """ Sends a packet through the specified port and IP address. Args: port: The port assigned to the connection that will be used to send the packet. packet: The packet to send. client_IP: the client's ipv4 address client_port: the client's port @attention: The last two parameters can only be used in server connections. In this case, - if both are None, the packet will be sent to all the clients (MULTICAST mode) - otherwise, the pacekt will be sent to a specific client (UNICAST mode) Returns: None if the packet was successfully sent and an error message if it wasn't. Raises: Nothing @attention: If the connection is not ready, the packet will be discarded. So PLEASE, check the connection's status BEFORE using it. """ if not self.__connectionPool.has_key((host, port)) : return "There's nothing attached to the port " + str(port) connection = self.__connectionPool[(host, port)] if connection.isReady() : connection.registerPacket() self.__outgoingDataQueue.queue(packet.getPriority(), (connection, packet, client_IP, client_port)) return None else : return "The connection is not ready yet" def createPacket(self, priority): """ Creates an empty data packet and returns it Args: priority: The new packet's priority. Returns: a new data packet. Raises: NetworkManagerException: this exception will be raised when the packet's priority is not a positive integer. """ if not isinstance(priority, int) or priority < 0 : raise NetworkManagerException("Data packets\' priorities MUST be positive integers") p = _Packet(Packet_TYPE.DATA, priority) return p def closeConnection(self, host, port): """ Closes a connection Args: port: The port assigned to the connection. If it's free, a NetworkManagerException will be raised. Returns: Nothing """ if not self.__connectionPool.has_key((host, port)) : raise NetworkManagerException("There's nothing attached to the port " + str(port)) # Retrieve the connection connection = self.__connectionPool[(host, port)] # Ask the connection to close connection.close() @staticmethod def printConnectionWarningIfNecessary(ip, port, packet_type, errorMessage): if (errorMessage != None) : print "Warning: unable to send {2} to {0}:{1}.".format(ip, port, packet_type) print "\t" + errorMessage