def __init__(self, certificatesDirectory=None, createReactorThread=True): """ Initializes the NetworkManager's state. Args: certificatesDirectory: the directory where the files server.crt and server.key are. createReactorThread: if True, a dedicated reactor thread will be created. If false, this thread won't be created. """ createReactorThread = not reactor.running self.__connectionPool = GenericThreadSafeDictionary() self.__outgoingDataQueue = GenericThreadSafePriorityQueue() if (createReactorThread): 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, createReactorThread = True): """ Initializes the NetworkManager's state. Args: certificatesDirectory: the directory where the files server.crt and server.key are. createReactorThread: if True, a dedicated reactor thread will be created. If false, this thread won't be created. """ createReactorThread = not reactor.running self.__connectionPool = GenericThreadSafeDictionary() self.__outgoingDataQueue = GenericThreadSafePriorityQueue() if (createReactorThread) : self.__networkThread = _TwistedReactorThread() else : self.__networkThread = None self.__outgoingDataThread = _OutgoingDataThread(self.__outgoingDataQueue) self.__connectionThread = _ConnectionMonitoringThread(self.__connectionPool) self.__certificatesDirectory = certificatesDirectory
class NetworkManager(): """ This class provides a facade to use Twisted in a higher abstraction level way. @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, createReactorThread = True): """ Initializes the NetworkManager's state. Args: certificatesDirectory: the directory where the files server.crt and server.key are. createReactorThread: if True, a dedicated reactor thread will be created. If false, this thread won't be created. """ createReactorThread = not reactor.running self.__connectionPool = GenericThreadSafeDictionary() self.__outgoingDataQueue = GenericThreadSafePriorityQueue() if (createReactorThread) : 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.getIPAddress(), 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=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. 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 and configure the endpoint factory = _CygnusCloudProtocolFactory(queue) if (not useSSL) : endpoint = TCP4ClientEndpoint(reactor, host, port, timeout, None) else : keyPath = self.__certificatesDirectory + "/" + "server.key" certificatePath = self.__certificatesDirectory + "/" + "server.crt" try : endpoint = SSL4ClientEndpoint(reactor, host, port, ssl.DefaultOpenSSLContextFactory(keyPath, certificatePath)) except Exception: raise NetworkManagerException("The key, the certificate or both were not found") endpoint.connect(factory) # Wait until the connection is ready time = 0 while factory.isDisconnected() and time <= timeout: sleep(0.01) time += 0.01 if factory.isDisconnected() : raise NetworkManagerException("The host " + host + ":" + str(port) +" seems to be unreachable") # Create the new connection connection = _NetworkConnection(False, host, port, factory, queue, thread, callbackObject) # Add the new connection to the connection pool self.__connectionPool[(host, port)] = connection 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. Returns: Nothing """ if self.__connectionPool.has_key(('127.0.0.1', 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 and configure the endpoint if (not useSSL) : endpoint = TCP4ServerEndpoint(reactor, port) else : keyPath = self.__certificatesDirectory + "/" + "server.key" certificatePath = self.__certificatesDirectory + "/" + "server.crt" try : endpoint = SSL4ServerEndpoint(reactor, port, ssl.DefaultOpenSSLContextFactory(keyPath, certificatePath)) except Exception: raise NetworkManagerException("The key, the certificate or both were not found") factory = _CygnusCloudProtocolFactory(queue) # Create the connection connection = _NetworkConnection(True, '', port, factory, queue, thread, callbackObject) # Create the deferred to retrieve the IListeningPort object def registerListeningPort(port): connection.setListeningPort(port) def onError(failure): connection.setError(failure) deferred = endpoint.listen(factory) deferred.addCallback(registerListeningPort) deferred.addErrback(onError) connection.setDeferred(deferred) # Register the new connection self.__connectionPool[('', port)] = connection 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.getIPAddress(), connection.getPort())) raise NetworkManagerException("Error: " + connection.getError()) if (connection.wasUnexpectedlyClosed()): self.__connectionPool.pop((connection.getIPAddress(), connection.getPort())) raise NetworkManagerException("Error: " + "The connection was closed abnormally") def sendPacket(self, host, port, packet): """ 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. Returns: Nothing 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 connection is a server connection and it's not ready yet, or - if the supplied port is free """ if not self.__connectionPool.has_key((host, port)) : raise NetworkManagerException("There's nothing attached to the port " + str(port)) connection = self.__connectionPool[(host, port)] self.__detectConnectionErrors(connection) if not connection.isReady() : if (connection.isServerConnection()) : raise NetworkManagerException("No clients have connected to this port yet") else : raise NetworkManagerException("The connection is not ready yet") connection.registerPacket() self.__outgoingDataQueue.queue(packet.getPriority(), (connection, packet)) 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()
class NetworkManager(): """ This class provides a facade to use Twisted in a higher abstraction level way. @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, createReactorThread=True): """ Initializes the NetworkManager's state. Args: certificatesDirectory: the directory where the files server.crt and server.key are. createReactorThread: if True, a dedicated reactor thread will be created. If false, this thread won't be created. """ createReactorThread = not reactor.running self.__connectionPool = GenericThreadSafeDictionary() self.__outgoingDataQueue = GenericThreadSafePriorityQueue() if (createReactorThread): 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.getIPAddress(), 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=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. 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 and configure the endpoint factory = _CygnusCloudProtocolFactory(queue) if (not useSSL): endpoint = TCP4ClientEndpoint(reactor, host, port, timeout, None) else: keyPath = self.__certificatesDirectory + "/" + "server.key" certificatePath = self.__certificatesDirectory + "/" + "server.crt" try: endpoint = SSL4ClientEndpoint( reactor, host, port, ssl.DefaultOpenSSLContextFactory(keyPath, certificatePath)) except Exception: raise NetworkManagerException( "The key, the certificate or both were not found") endpoint.connect(factory) # Wait until the connection is ready time = 0 while factory.isDisconnected() and time <= timeout: sleep(0.01) time += 0.01 if factory.isDisconnected(): raise NetworkManagerException("The host " + host + ":" + str(port) + " seems to be unreachable") # Create the new connection connection = _NetworkConnection(False, host, port, factory, queue, thread, callbackObject) # Add the new connection to the connection pool self.__connectionPool[(host, port)] = connection 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. Returns: Nothing """ if self.__connectionPool.has_key(('127.0.0.1', 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 and configure the endpoint if (not useSSL): endpoint = TCP4ServerEndpoint(reactor, port) else: keyPath = self.__certificatesDirectory + "/" + "server.key" certificatePath = self.__certificatesDirectory + "/" + "server.crt" try: endpoint = SSL4ServerEndpoint( reactor, port, ssl.DefaultOpenSSLContextFactory(keyPath, certificatePath)) except Exception: raise NetworkManagerException( "The key, the certificate or both were not found") factory = _CygnusCloudProtocolFactory(queue) # Create the connection connection = _NetworkConnection(True, '', port, factory, queue, thread, callbackObject) # Create the deferred to retrieve the IListeningPort object def registerListeningPort(port): connection.setListeningPort(port) def onError(failure): connection.setError(failure) deferred = endpoint.listen(factory) deferred.addCallback(registerListeningPort) deferred.addErrback(onError) connection.setDeferred(deferred) # Register the new connection self.__connectionPool[('', port)] = connection 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.getIPAddress(), connection.getPort())) raise NetworkManagerException("Error: " + connection.getError()) if (connection.wasUnexpectedlyClosed()): self.__connectionPool.pop( (connection.getIPAddress(), connection.getPort())) raise NetworkManagerException( "Error: " + "The connection was closed abnormally") def sendPacket(self, host, port, packet): """ 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. Returns: Nothing 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 connection is a server connection and it's not ready yet, or - if the supplied port is free """ if not self.__connectionPool.has_key((host, port)): raise NetworkManagerException( "There's nothing attached to the port " + str(port)) connection = self.__connectionPool[(host, port)] self.__detectConnectionErrors(connection) if not connection.isReady(): if (connection.isServerConnection()): raise NetworkManagerException( "No clients have connected to this port yet") else: raise NetworkManagerException( "The connection is not ready yet") connection.registerPacket() self.__outgoingDataQueue.queue(packet.getPriority(), (connection, packet)) 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()