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 __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)
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)
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)