Exemple #1
0
    def __init__(self, context, name=""):

        # Variables to hold threads
        self._sendingThread = None
        self._receivingThread = None

        # Create queue for outgoing packages.
        self._qout = TinyPackageQueue(64, *context._queue_params)

        # Init normally (calls _set_status(0)
        Connection.__init__(self, context, name)
Exemple #2
0
 def __init__(self, context, name=''):
     
     # Variables to hold threads
     self._sendingThread = None
     self._receivingThread = None
     
     # Create queue for outgoing packages.
     self._qout = TinyPackageQueue(64, *context._queue_params)
     
     # Init normally (calls _set_status(0)
     Connection.__init__(self, context, name)
Exemple #3
0
class TcpConnection(Connection):
    """TcpConnection(context, name='')

    The TcpConnection class implements a connection between two
    contexts that are in differenr processes or on different machines
    connected via the internet.

    This class handles the low-level communication for the context.
    A ContextConnection instance wraps a single BSD socket for its
    communication, and uses TCP/IP as the underlying communication
    protocol. A persisten connection is used (the BSD sockets stay
    connected). This allows to better distinguish between connection
    problems and timeouts caused by the other side being busy.

    """
    def __init__(self, context, name=""):

        # Variables to hold threads
        self._sendingThread = None
        self._receivingThread = None

        # Create queue for outgoing packages.
        self._qout = TinyPackageQueue(64, *context._queue_params)

        # Init normally (calls _set_status(0)
        Connection.__init__(self, context, name)

    def _set_status(self, status, bsd_socket=None):
        """_connected(status, bsd_socket=None)

        This method is called when a connection is made.

        Private method to apply the bsd_socket.
        Sets the socket and updates the status.
        Also instantiates the IO threads.

        """

        # Lock the connection while we change its status
        self._lock.acquire()

        # Update hostname and port number; for hosting connections the port
        # may be different if max_tries > 0. Each client connection will be
        # assigned a different ephemeral port number.
        # http://www.tcpipguide.com/free/t_TCPPortsConnectionsandConnectionIdentification-2.htm
        # Also get hostname and port for other end
        if bsd_socket is not None:
            if True:
                self._hostname1, self._port1 = bsd_socket.getsockname()
            if status != STATUS_WAITING:
                self._hostname2, self._port2 = bsd_socket.getpeername()

        # Set status as normal
        Connection._set_status(self, status)

        try:

            if status in [STATUS_HOSTING, STATUS_CONNECTED]:
                # Really connected

                # Store socket
                self._bsd_socket = bsd_socket

                # Set socket to blocking. Needed explicitly on Windows
                # One of these things it takes you hours to find out ...
                bsd_socket.setblocking(1)

                # Create and start io threads
                self._sendingThread = SendingThread(self)
                self._receivingThread = ReceivingThread(self)
                #
                self._sendingThread.start()
                self._receivingThread.start()

            if status == 0:

                # Close bsd socket
                try:
                    self._bsd_socket.shutdown()
                except Exception:
                    pass
                try:
                    self._bsd_socket.close()
                except Exception:
                    pass
                self._bsd_socket = None

                # Remove references to threads
                self._sendingThread = None
                self._receivingThread = None

        finally:
            self._lock.release()

    def _bind(self, hostname, port, max_tries=1):
        """Bind the bsd socket. Launches a dedicated thread that waits
        for incoming connections and to do the handshaking procedure.
        """

        # Create socket.
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

        # Set buffer size to be fairly small (less than 10 packages)
        s.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, SOCKET_BUFFERS_SIZE)
        s.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, SOCKET_BUFFERS_SIZE)

        # Apply SO_REUSEADDR when binding, so that an improperly closed
        # socket on the same port will not prevent us from connecting.
        # It also allows a connection to bind at the same port number,
        # but only after the previous binding connection has connected
        # (and has closed the listen-socket).
        #
        # SO_REUSEADDR means something different on win32 than it does
        # for Linux sockets. To get the intended behavior on Windows,
        # we don't have to do anything. Also see:
        #  * http://msdn.microsoft.com/en-us/library/ms740621%28VS.85%29.aspx
        #  * http://twistedmatrix.com/trac/ticket/1151
        #  * http://www.tcpipguide.com/free/t_TCPPortsConnectionsandConnectionIdentification-2.htm
        if not sys.platform.startswith("win"):
            s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

        # Try all ports in the specified range
        for try_count in range(1, max_tries + 1):
            port += int(try_count**0.5)
            try:
                s.bind((hostname, port))
                break
            except Exception:
                # Raise the socket exception if we were asked to try
                # just one port. Otherwise just try the next
                if max_tries == 1:
                    raise
                continue
        else:
            # We tried all ports without success
            tmp = "Could not bind to any of the %i %i ports tried."
            raise IOError(tmp % (max_tries, port))

        # Tell the socket it is a host. Backlog of at least 1 to avoid linux
        # kernel from detecting SYN flood and throttling the connection (#381)
        s.listen(1)

        # Set connected (status 1: waiting for connection)
        # Will be called with status 2 by the hostThread on success
        self._set_status(STATUS_WAITING, s)

        # Start thread to wait for a connection
        # (keep reference so the thread-object stays alive)
        self._hostThread = HostThread(self, s)
        self._hostThread.start()

    def _connect(self, hostname, port, timeout=1.0):
        """Connect to a bound socket."""

        # Create socket
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

        # Set buffer size to be fairly small (less than 10 packages)
        s.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, SOCKET_BUFFERS_SIZE)
        s.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, SOCKET_BUFFERS_SIZE)

        # Refuse rediculously low timeouts
        if timeout <= 0.01:
            timeout = 0.01

        # Try to connect
        ok = False
        timestamp = time.time() + timeout
        while not ok and time.time() < timestamp:
            try:
                s.connect((hostname, port))
                ok = True
            except socket.error:
                pass
            except socket.timeout:
                pass
            time.sleep(timeout / 100.0)

        # Did it work?
        if not ok:
            type, value, tb = sys.exc_info()
            del tb
            err = str(value)
            raise IOError("Cannot connect to %s on %i: %s" %
                          (hostname, port, err))

        # Shake hands
        h = HandShaker(s)
        success, info = h.shake_hands_as_client(self.id1)

        # Problem?
        if not success:
            self._set_status(0)
            if not info:
                info = "problem during handshake"
            raise IOError("Could not connect: " + info)

        # Store id
        self._id2, self._pid2 = info

        # Set connected (status 3: connected as client)
        self._set_status(STATUS_CONNECTED, s)

    def _notify_other_end_of_closing(self):
        """Send PACKAGE_CLOSE."""
        self._qout.push(PACKAGE_CLOSE)
        try:
            self.flush(1.0)
        except Exception:
            pass  # Well, we tried ...

    def _flush(self, timeout=5.0):
        """Put a dummy message on the queue and spinlock until
        the thread has popped it from the queue.
        """

        # Check
        if not self.is_connected:
            RuntimeError("Cannot flush if not connected.")

        # Put dummy package on the queue
        self._qout.push(PACKAGE_HEARTBEAT)

        # Wait for the queue to empty. If it is, the heart beat package
        # may not been send yet, but every package befor it is.
        timestamp = time.time() + timeout
        while self.is_connected and not self._qout.empty():
            time.sleep(0.01)
            if time.time() > timestamp:
                raise RuntimeError("Sending the packages timed out.")

    def _send_package(self, package):
        """Put package on the queue, where the sending thread will
        pick it up.
        """
        self._qout.push(package)

    def _inject_package(self, package):
        """Put package in queue, but bypass potential blocking."""
        self._qout._q.append(package)
Exemple #4
0
class TcpConnection(Connection):
    """ TcpConnection(context, name='')
    
    The TcpConnection class implements a connection between two
    contexts that are in differenr processes or on different machines
    connected via the internet.
    
    This class handles the low-level communication for the context.
    A ContextConnection instance wraps a single BSD socket for its
    communication, and uses TCP/IP as the underlying communication
    protocol. A persisten connection is used (the BSD sockets stay
    connected). This allows to better distinguish between connection
    problems and timeouts caused by the other side being busy.
    
    """
    
    def __init__(self, context, name=''):
        
        # Variables to hold threads
        self._sendingThread = None
        self._receivingThread = None
        
        # Create queue for outgoing packages.
        self._qout = TinyPackageQueue(64, *context._queue_params)
        
        # Init normally (calls _set_status(0)
        Connection.__init__(self, context, name)
    
    
    def _set_status(self, status, bsd_socket=None):
        """ _connected(status, bsd_socket=None)
        
        This method is called when a connection is made.
        
        Private method to apply the bsd_socket.
        Sets the socket and updates the status.
        Also instantiates the IO threads.
        
        """
        
        # Lock the connection while we change its status
        self._lock.acquire()
        
        # Update hostname and port number; for hosting connections the port
        # may be different if max_tries > 0. Each client connection will be
        # assigned a different ephemeral port number.
        # http://www.tcpipguide.com/free/t_TCPPortsConnectionsandConnectionIdentification-2.htm
        # Also get hostname and port for other end
        if bsd_socket is not None:
            if True:
                self._hostname1, self._port1 = bsd_socket.getsockname()
            if status != STATUS_WAITING:
                self._hostname2, self._port2 = bsd_socket.getpeername()
        
        # Set status as normal
        Connection._set_status(self, status)
        
        try:
            
            if status in [STATUS_HOSTING, STATUS_CONNECTED]:
                # Really connected
                
                # Store socket
                self._bsd_socket = bsd_socket
                
                # Set socket to blocking. Needed explicitly on Windows
                # One of these things it takes you hours to find out ...
                bsd_socket.setblocking(1)
                
                # Create and start io threads
                self._sendingThread = SendingThread(self)
                self._receivingThread = ReceivingThread(self)
                #
                self._sendingThread.start()
                self._receivingThread.start()
            
            if status == 0:
                
                # Close bsd socket
                try:
                    self._bsd_socket.shutdown()
                except Exception:
                    pass
                try:
                    self._bsd_socket.close()
                except Exception:
                    pass
                self._bsd_socket = None
                
                # Remove references to threads
                self._sendingThread = None
                self._receivingThread = None
        
        finally:
            self._lock.release()
    
    
    def _bind(self, hostname, port, max_tries=1):
        """ Bind the bsd socket. Launches a dedicated thread that waits
        for incoming connections and to do the handshaking procedure.
        """
        
        # Create socket.
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        
        # Set buffer size to be fairly small (less than 10 packages)
        s.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, SOCKET_BUFFERS_SIZE)
        s.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, SOCKET_BUFFERS_SIZE)
        
        # Apply SO_REUSEADDR when binding, so that an improperly closed
        # socket on the same port will not prevent us from connecting.
        # It also allows a connection to bind at the same port number,
        # but only after the previous binding connection has connected
        # (and has closed the listen-socket).
        #
        # SO_REUSEADDR means something different on win32 than it does
        # for Linux sockets. To get the intended behavior on Windows,
        # we don't have to do anything. Also see:
        #  * http://msdn.microsoft.com/en-us/library/ms740621%28VS.85%29.aspx
        #  * http://twistedmatrix.com/trac/ticket/1151
        #  * http://www.tcpipguide.com/free/t_TCPPortsConnectionsandConnectionIdentification-2.htm
        if not sys.platform.startswith('win'):
            s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        
        # Try all ports in the specified range
        for port2 in range(port, port+max_tries):
            try:
                s.bind((hostname,port2))
                break
            except Exception:
                # Raise the socket exception if we were asked to try
                # just one port. Otherwise just try the next
                if max_tries == 1:
                    raise
                continue
        else:
            # We tried all ports without success
            tmp = str(max_tries)
            tmp = "Could not bind to any of the " + tmp + " ports tried."
            raise IOError(tmp)
        
        # Tell the socket it is a host. Backlog of at least 1 to avoid linux
        # kernel from detecting SYN flood and throttling the connection (#381)
        s.listen(1)
        
        # Set connected (status 1: waiting for connection)
        # Will be called with status 2 by the hostThread on success
        self._set_status(STATUS_WAITING, s)
        
        # Start thread to wait for a connection
        # (keep reference so the thread-object stays alive)
        self._hostThread = HostThread(self, s)
        self._hostThread.start()
    
    
    def _connect(self, hostname, port, timeout=1.0):
        """ Connect to a bound socket.
        """
        
        # Create socket
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        
        # Set buffer size to be fairly small (less than 10 packages)
        s.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, SOCKET_BUFFERS_SIZE)
        s.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, SOCKET_BUFFERS_SIZE)
        
        # Refuse rediculously low timeouts
        if timeout<= 0.01:
            timeout = 0.01
        
        # Try to connect
        ok = False
        timestamp = time.time() + timeout
        while not ok and time.time()<timestamp:
            try:
                s.connect((hostname, port))
                ok = True
            except socket.error:
                pass
            except socket.timeout:
                pass
            time.sleep(timeout/100.0)
        
        # Did it work?
        if not ok:
            type, value, tb = sys.exc_info()
            del tb
            err = str(value)
            raise IOError("Cannot connect to %s on %i: %s" % (hostname, port, err))
        
        # Shake hands
        h = HandShaker(s)
        success, info = h.shake_hands_as_client(self.id1)
        
        # Problem?
        if not success:
            self._set_status(0)
            if not info:
                info = 'problem during handshake'
            raise IOError('Could not connect: '+ info)
        
        # Store id
        self._id2, self._pid2 = info
        
        # Set connected (status 3: connected as client)
        self._set_status(STATUS_CONNECTED, s)
    
    
    def _notify_other_end_of_closing(self):
        """ Send PACKAGE_CLOSE.
        """
        self._qout.push(PACKAGE_CLOSE)
        try:
            self.flush(1.0)
        except Exception:
            pass # Well, we tried ...
    
    
    def _flush(self, timeout=5.0):
        """ Put a dummy message on the queue and spinlock until
        the thread has popped it from the queue.
        """
        
        # Check
        if not self.is_connected:
            RuntimeError('Cannot flush if not connected.')
        
        # Put dummy package on the queue
        self._qout.push(PACKAGE_HEARTBEAT)
        
        # Wait for the queue to empty. If it is, the heart beat package
        # may not been send yet, but every package befor it is.
        timestamp = time.time() + timeout
        while self.is_connected and not self._qout.empty():
            time.sleep(0.01)
            if time.time() > timestamp:
                raise RuntimeError('Sending the packages timed out.')
    
    
    def _send_package(self, package):
        """ Put package on the queue, where the sending thread will
        pick it up.
        """
        self._qout.push(package)
    
    
    def _inject_package(self, package):
        """ Put package in queue, but bypass potential blocking.
        """
        self._qout._q.append(package)