Esempio n. 1
0
    def connect(self,
        host=None,
        port=None,
        timeLim=None,
    ):
        """Open the connection.

        Inputs:
        - host: IP address (name or numeric) of host; if omitted, the default is used
        - port: port number; if omitted, the default is used
        - timeLim: time limit (sec); if None then no time limit
        
        Raise RuntimeError if:
        - already connecting or connected
        - host omitted and self.host not already set
        """
        if not self.mayConnect:
            raise RuntimeError("Cannot connect: already connecting or connected")
        if not (host or self.host):
            raise RuntimeError("Cannot connect: no host specified")

        self.host = host or self.host
        self.port = port or self.port
        
        self._sock.setStateCallback() # remove socket state callback
        if not self._sock.isDone:
            self._sock.close()

        self._sock = TCPSocket(
            host = self.host,
            port = self.port,
            stateCallback = self._sockStateCallback,
            timeLim = timeLim,
            name = self._name,
        )
        
        if self._authReadCallback:
            self._localSocketStateDict[TCPSocket.Connected] = self.Authorizing
            self._setRead(True)
        else:
            self._localSocketStateDict[TCPSocket.Connected] = self.Connected
            self._setRead(False)
Esempio n. 2
0
    def connect(self,
        host=None,
        port=None,
        timeLim=None,
    ):
        """Open the connection.

        Inputs:
        - host: IP address (name or numeric) of host; if omitted, the default is used
        - port: port number; if omitted, the default is used
        - timeLim: time limit (sec); if None then no time limit

        Raise RuntimeError if:
        - already connecting or connected
        - host omitted and self.host not already set
        """
        if not self.mayConnect:
            raise RuntimeError("Cannot connect: already connecting or connected")
        if not (host or self.host):
            raise RuntimeError("Cannot connect: no host specified")

        self.host = host or self.host
        self.port = port or self.port

        self._sock.setStateCallback() # remove socket state callback
        if not self._sock.isDone:
            self._sock.close()

        self._sock = TCPSocket(
            host = self.host,
            port = self.port,
            stateCallback = self._sockStateCallback,
            timeLim = timeLim,
            name = self._name,
            lineTerminator = self.lineTerminator,
        )

        if self._authReadCallback:
            self._localSocketStateDict[TCPSocket.Connected] = self.Authorizing
            self._setRead(True)
        else:
            self._localSocketStateDict[TCPSocket.Connected] = self.Connected
            self._setRead(False)
Esempio n. 3
0
class TCPConnection(object):
    """A TCP Socket with the ability to disconnect and reconnect.
    Optionally returns read data as lines
    and has hooks for authorization.
    """
    # states
    Connecting = "Connecting"
    Authorizing = "Authorizing"
    Connected = "Connected"
    Disconnecting = "Disconnecting"
    Failing = "Failing"
    Disconnected = "Disconnected"
    Failed = "Failed"

    _AllStates = set((
        Connecting,
        Authorizing,
        Connected,
        Disconnecting,
        Failing,
        Disconnected,
        Failed,
    ))
    _ConnectedStates = set((Connected,))
    _DisconnectedStates = set((Disconnected, Failed))
    _DoneStates = set((Connected, Disconnected, Failed))
    _FailedStates = set((Failed,))
    
    def __init__(self,
        host = None,
        port = 23,
        readCallback = None,
        readLines = False,
        stateCallback = None,
        authReadCallback = None,
        authReadLines = False,
        name = "",
    ):
        """Construct a TCPConnection

        Inputs:
        - host: initial host (can be changed when connecting)
        - port: initial port (can be changed when connecting);
          defaults to 23, the standard telnet port
        - readCallback: function to call whenever data is read;
          see addReadCallback for details.
        - readLines: if True, the read callbacks receive entire lines
            minus the terminator; otherwise the data is distributed as received
        - stateCallback: a function to call whenever the state or reason changes;
          see addStateCallback for details.
        - authReadCallback: if specified, used as the initial read callback function;
            if auth succeeds, it must call self._authDone()
        - authReadLines: if True, the auth read callback receives entire lines
        - name: a string to identify this object; strictly optional
        """        
        self.host = host
        self.port = port
        self._readLines = bool(readLines)
        self._userReadCallbacks = []
        if readCallback:
            self.addReadCallback(readCallback)
        self._stateCallbacks = []
        if stateCallback:
            self.addStateCallback(stateCallback)
        self._authReadLines = bool(authReadLines)
        self._authReadCallback = authReadCallback
        self._name = name

        self._state = self.Disconnected
        self._reason = ""
        self._currReadCallbacks = []
        
        # translation table from TCPSocket states to local states
        # note that the translation of Connected will depend
        # on whether there is authorization; this initial setup
        # assumes no authorization
        if self._authReadCallback:
            locConnected = self.Authorizing
        else:
            locConnected = self.Connected
        self._localSocketStateDict = {
            TCPSocket.Connecting: self.Connecting,
            TCPSocket.Connected: locConnected,
            TCPSocket.Closing: self.Disconnecting,
            TCPSocket.Failing: self.Failing,
            TCPSocket.Closed: self.Disconnected,
            TCPSocket.Failed: self.Failed,
        }
        
        self._sock = NullTCPSocket(name=name, host=host, port=port)
        
    def addReadCallback(self, readCallback):
        """Add a read function, to be called whenever data is read.
        
        Inputs:
        - readCallback: function to call whenever a line of data is read;
          it is sent two arguments:
          - the socket (a TCPSocket object)
          - the data read; in line mode the line terminator is stripped
        """
        assert callable(readCallback), "read callback not callable"
        self._userReadCallbacks.append(readCallback)
    
    def addStateCallback(self, stateCallback, callNow=False):
        """Add a state function to call whenever the state or reason changes.
        
        Inputs:
        - stateCallback: the function; it is sent one argument: this TCPConnection
        - callNow: call the connection function immediately?
        """
        assert callable(stateCallback)
        self._stateCallbacks.append(stateCallback)
        if callNow:
            stateCallback(self)

    def connect(self,
        host=None,
        port=None,
        timeLim=None,
    ):
        """Open the connection.

        Inputs:
        - host: IP address (name or numeric) of host; if omitted, the default is used
        - port: port number; if omitted, the default is used
        - timeLim: time limit (sec); if None then no time limit
        
        Raise RuntimeError if:
        - already connecting or connected
        - host omitted and self.host not already set
        """
        if not self.mayConnect:
            raise RuntimeError("Cannot connect: already connecting or connected")
        if not (host or self.host):
            raise RuntimeError("Cannot connect: no host specified")

        self.host = host or self.host
        self.port = port or self.port
        
        self._sock.setStateCallback() # remove socket state callback
        if not self._sock.isDone:
            self._sock.close()

        self._sock = TCPSocket(
            host = self.host,
            port = self.port,
            stateCallback = self._sockStateCallback,
            timeLim = timeLim,
            name = self._name,
        )
        
        if self._authReadCallback:
            self._localSocketStateDict[TCPSocket.Connected] = self.Authorizing
            self._setRead(True)
        else:
            self._localSocketStateDict[TCPSocket.Connected] = self.Connected
            self._setRead(False)
    
    def disconnect(self, isOK=True, reason=None):
        """Close the connection.

        Called disconnect instead of close (the usual counterpoint in the socket library)
        because you can reconnect at any time by calling connect.
        
        Inputs:
        - isOK: if True, final state is Disconnected, else Failed
        - reason: a string explaining why, or None to leave unchanged;
            please specify a reason if isOK is false!   
        """
        self._sock.close(isOK=isOK, reason=reason)

    @property
    def fullState(self):
        """Returns the current state as a tuple:
        - state: the state, as a string
        - reason: the reason for the state ("" if none)
        """
        return (self._state, self._reason)

    @property
    def state(self):
        """Returns the current state as a string.
        """
        return self._state
    
    @property
    def isConnected(self):
        """Return True if connected, False otherwise.
        """
        return self._state in self._ConnectedStates
    
    @property
    def isDisconnected(self):
        """Return True if fully disconnected, False otherwise.
        """
        return self._state in self._DisconnectedStates

    @property
    def isDone(self):
        """Return True if the last transition is finished, i.e. connected, disconnected or failed.
        """
        return self._state in self._DoneStates
    
    @property
    def didFail(self):
        """Return True if the connection failed
        """
        return self._state in self._FailedStates
    
    @property
    def mayConnect(self):
        """Return True if one may call connect, false otherwise"""
        return self._state not in (self.Connected, self.Connecting, self.Authorizing)

    def removeReadCallback(self, readCallback):
        """Attempt to remove the read callback function;

        Returns True if successful, False if the subr was not found in the list.
        """
        try:
            self._userReadCallbacks.remove(readCallback)
            return True
        except ValueError:
            return False

    def removeStateCallback(self, stateCallback):
        """Attempt to remove the state callback function;

        Returns True if successful, False if the subr was not found in the list.
        """
        try:
            self._stateCallbacks.remove(stateCallback)
            return True
        except ValueError:
            return False
    
    def write(self, astr):
        """Write data to the socket. Does not block.
        
        Safe to call as soon as you call connect, but of course
        no data is sent until the connection is made.
        
        Raises UnicodeError if the data cannot be expressed as ascii.
        Raises RuntimeError if the socket is not connecting or connected.
        If an error occurs while sending the data, the socket is closed,
        the state is set to Failed and _reason is set.
        """
        self._sock.write(astr)

    def writeLine(self, astr):
        """Send a line of data, appending newline.

        Raises UnicodeError if the data cannot be expressed as ascii.
        Raises RuntimeError if the socket is not connecting or connected.
        If an error occurs while sending the data, the socket is closed,
        the state is set to Failed and _reason is set.
        """
        self._sock.writeLine(astr)
    
    def _authDone(self, msg=""):
        """Call from your authorization callback function
        when authorization succeeds.
        Do not call unless you specified an authorization callback function.
        
        If authorization fails, call self.disconnect(False, error msg) instead.
        """
        self._setRead(forAuth=False)
        self._setState(self.Connected, msg)
    
    def _setRead(self, forAuth=False):
        """Set up reads.
        """
        if (forAuth and self._authReadLines) or (not forAuth and self._readLines):
            self._sock.setReadCallback(self._sockReadLineCallback)
        else:
            self._sock.setReadCallback(self._sockReadCallback)
        if forAuth:
            self._currReadCallbacks = [self._authReadCallback,]
        else:
            self._currReadCallbacks = self._userReadCallbacks

    def _setState(self, newState, reason=None):
        """Set the state and reason. If anything has changed, call the state callback functions.

        Inputs:
        - newState  one of the state constants defined at top of file
        - reason    the reason for the change (a string, or None to leave unchanged)
        """
        #print "_setState(newState=%s, reason=%s); self._stateCallbacks=%s" % (newState, reason, self._stateCallbacks)
        oldStateReason = (self._state, self._reason)
        if newState not in self._AllStates:
            raise RuntimeError("unknown connection state: %s" % (newState,))
        self._state = newState
        if reason is not None:
            self._reason = str(reason)
        
        # if the state or reason has changed, call state callbacks
        if oldStateReason != (self._state, self._reason):
            for stateCallback in self._stateCallbacks:
                safeCall2("%s._setState" % (self,), stateCallback, self)
    
    def _sockReadCallback(self, sock):
        """Read callback for the socket in binary mode (not line mode).
                
        When data is received, read it and issues all callbacks.
        """
        dataRead = sock.read()
        #print "%s._sockReadCallback(sock=%r) called; data=%r" % (self, sock, dataRead)
        for subr in self._currReadCallbacks:
            subr(sock, dataRead)

    def _sockReadLineCallback(self, sock):
        """Read callback for the socket in line mode.
                
        Whenever a line is received, issues all callbacks, first stripping the line terminator.
        """
        dataRead = sock.readLine()
        if dataRead is None:
            # only a partial line was available
            return
        #print "%s._sockReadLineCallback(sock=%r) called with data %r" % (self, sock, dataRead)
        for subr in self._currReadCallbacks:
            subr(sock, dataRead)
    
    def _sockStateCallback(self, sock):
        sockState, reason = sock.fullState
        try:
            locState = self._localSocketStateDict[sockState]
        except KeyError:
            sys.stderr.write("unknown TCPSocket state %r\n" % sockState)
            return
        self._setState(locState, reason)

    def _getArgStr(self):
        """Return main arguments as a string, for __str__
        """
        return "name=%r" % (self._name)

    def __str__(self):
        return "%s(%s)" % (self.__class__.__name__, self._getArgStr())
Esempio n. 4
0
    def __init__(self,
        host = None,
        port = 23,
        readCallback = None,
        readLines = False,
        stateCallback = None,
        authReadCallback = None,
        authReadLines = False,
        name = "",
    ):
        """Construct a TCPConnection

        Inputs:
        - host: initial host (can be changed when connecting)
        - port: initial port (can be changed when connecting);
          defaults to 23, the standard telnet port
        - readCallback: function to call whenever data is read;
          see addReadCallback for details.
        - readLines: if True, the read callbacks receive entire lines
            minus the terminator; otherwise the data is distributed as received
        - stateCallback: a function to call whenever the state or reason changes;
          see addStateCallback for details.
        - authReadCallback: if specified, used as the initial read callback function;
            if auth succeeds, it must call self._authDone()
        - authReadLines: if True, the auth read callback receives entire lines
        - name: a string to identify this object; strictly optional
        """        
        self.host = host
        self.port = port
        self._readLines = bool(readLines)
        self._userReadCallbacks = []
        if readCallback:
            self.addReadCallback(readCallback)
        self._stateCallbacks = []
        if stateCallback:
            self.addStateCallback(stateCallback)
        self._authReadLines = bool(authReadLines)
        self._authReadCallback = authReadCallback
        self._name = name

        self._state = self.Disconnected
        self._reason = ""
        self._currReadCallbacks = []
        
        # translation table from TCPSocket states to local states
        # note that the translation of Connected will depend
        # on whether there is authorization; this initial setup
        # assumes no authorization
        if self._authReadCallback:
            locConnected = self.Authorizing
        else:
            locConnected = self.Connected
        self._localSocketStateDict = {
            TCPSocket.Connecting: self.Connecting,
            TCPSocket.Connected: locConnected,
            TCPSocket.Closing: self.Disconnecting,
            TCPSocket.Failing: self.Failing,
            TCPSocket.Closed: self.Disconnected,
            TCPSocket.Failed: self.Failed,
        }
        
        self._sock = NullTCPSocket(name=name, host=host, port=port)
Esempio n. 5
0
    def __init__(self,
        host = None,
        port = 23,
        readCallback = None,
        readLines = False,
        stateCallback = None,
        authReadCallback = None,
        authReadLines = False,
        name = "",
    ):
        """Construct a TCPConnection

        Inputs:
        - host: initial host (can be changed when connecting)
        - port: initial port (can be changed when connecting);
          defaults to 23, the standard telnet port
        - readCallback: function to call whenever data is read;
          see addReadCallback for details.
        - readLines: if True, the read callbacks receive entire lines
            minus the terminator; otherwise the data is distributed as received
        - stateCallback: a function to call whenever the state or reason changes;
          see addStateCallback for details.
        - authReadCallback: if specified, used as the initial read callback function;
            if auth succeeds, it must call self._authDone()
        - authReadLines: if True, the auth read callback receives entire lines
        - name: a string to identify this object; strictly optional
        """        
        self.host = host
        self.port = port
        self._readLines = bool(readLines)
        self._userReadCallbacks = []
        if readCallback:
            self.addReadCallback(readCallback)
        self._stateCallbacks = []
        if stateCallback:
            self.addStateCallback(stateCallback)
        self._authReadLines = bool(authReadLines)
        self._authReadCallback = authReadCallback
        self._name = name

        self._state = self.Disconnected
        self._reason = ""
        self._currReadCallbacks = []
        
        # translation table from TCPSocket states to local states
        # note that the translation of Connected will depend
        # on whether there is authorization; this initial setup
        # assumes no authorization
        if self._authReadCallback:
            locConnected = self.Authorizing
        else:
            locConnected = self.Connected
        self._localSocketStateDict = {
            TCPSocket.Connecting: self.Connecting,
            TCPSocket.Connected: locConnected,
            TCPSocket.Closing: self.Disconnecting,
            TCPSocket.Failing: self.Failing,
            TCPSocket.Closed: self.Disconnected,
            TCPSocket.Failed: self.Failed,
        }
        
        self._sock = NullTCPSocket(name=name, host=host, port=port)
Esempio n. 6
0
class TCPConnection(object):
    """A TCP Socket with the ability to disconnect and reconnect.
    Optionally returns read data as lines
    and has hooks for authorization.
    """
    # states
    Connecting = "Connecting"
    Authorizing = "Authorizing"
    Connected = "Connected"
    Disconnecting = "Disconnecting"
    Failing = "Failing"
    Disconnected = "Disconnected"
    Failed = "Failed"

    _AllStates = set((
        Connecting,
        Authorizing,
        Connected,
        Disconnecting,
        Failing,
        Disconnected,
        Failed,
    ))
    _ConnectedStates = set((Connected,))
    _DisconnectedStates = set((Disconnected, Failed))
    _DoneStates = set((Connected, Disconnected, Failed))
    _FailedStates = set((Failed,))
    
    def __init__(self,
        host = None,
        port = 23,
        readCallback = None,
        readLines = False,
        stateCallback = None,
        authReadCallback = None,
        authReadLines = False,
        name = "",
    ):
        """Construct a TCPConnection

        Inputs:
        - host: initial host (can be changed when connecting)
        - port: initial port (can be changed when connecting);
          defaults to 23, the standard telnet port
        - readCallback: function to call whenever data is read;
          see addReadCallback for details.
        - readLines: if True, the read callbacks receive entire lines
            minus the terminator; otherwise the data is distributed as received
        - stateCallback: a function to call whenever the state or reason changes;
          see addStateCallback for details.
        - authReadCallback: if specified, used as the initial read callback function;
            if auth succeeds, it must call self._authDone()
        - authReadLines: if True, the auth read callback receives entire lines
        - name: a string to identify this object; strictly optional
        """        
        self.host = host
        self.port = port
        self._readLines = bool(readLines)
        self._userReadCallbacks = []
        if readCallback:
            self.addReadCallback(readCallback)
        self._stateCallbacks = []
        if stateCallback:
            self.addStateCallback(stateCallback)
        self._authReadLines = bool(authReadLines)
        self._authReadCallback = authReadCallback
        self._name = name

        self._state = self.Disconnected
        self._reason = ""
        self._currReadCallbacks = []
        
        # translation table from TCPSocket states to local states
        # note that the translation of Connected will depend
        # on whether there is authorization; this initial setup
        # assumes no authorization
        if self._authReadCallback:
            locConnected = self.Authorizing
        else:
            locConnected = self.Connected
        self._localSocketStateDict = {
            TCPSocket.Connecting: self.Connecting,
            TCPSocket.Connected: locConnected,
            TCPSocket.Closing: self.Disconnecting,
            TCPSocket.Failing: self.Failing,
            TCPSocket.Closed: self.Disconnected,
            TCPSocket.Failed: self.Failed,
        }
        
        self._sock = NullTCPSocket(name=name, host=host, port=port)
        
    def addReadCallback(self, readCallback):
        """Add a read function, to be called whenever data is read.
        
        Inputs:
        - readCallback: function to call whenever a line of data is read;
          it is sent two arguments:
          - the socket (a TCPSocket object)
          - the data read; in line mode the line terminator is stripped
        """
        assert isinstance(readCallback, collections.Callable), "read callback not callable"
        self._userReadCallbacks.append(readCallback)
    
    def addStateCallback(self, stateCallback, callNow=False):
        """Add a state function to call whenever the state or reason changes.
        
        Inputs:
        - stateCallback: the function; it is sent one argument: this TCPConnection
        - callNow: call the connection function immediately?
        """
        assert isinstance(stateCallback, collections.Callable)
        self._stateCallbacks.append(stateCallback)
        if callNow:
            stateCallback(self)

    def connect(self,
        host=None,
        port=None,
        timeLim=None,
    ):
        """Open the connection.

        Inputs:
        - host: IP address (name or numeric) of host; if omitted, the default is used
        - port: port number; if omitted, the default is used
        - timeLim: time limit (sec); if None then no time limit
        
        Raise RuntimeError if:
        - already connecting or connected
        - host omitted and self.host not already set
        """
        if not self.mayConnect:
            raise RuntimeError("Cannot connect: already connecting or connected")
        if not (host or self.host):
            raise RuntimeError("Cannot connect: no host specified")

        self.host = host or self.host
        self.port = port or self.port
        
        self._sock.setStateCallback() # remove socket state callback
        if not self._sock.isDone:
            self._sock.close()

        self._sock = TCPSocket(
            host = self.host,
            port = self.port,
            stateCallback = self._sockStateCallback,
            timeLim = timeLim,
            name = self._name,
        )
        
        if self._authReadCallback:
            self._localSocketStateDict[TCPSocket.Connected] = self.Authorizing
            self._setRead(True)
        else:
            self._localSocketStateDict[TCPSocket.Connected] = self.Connected
            self._setRead(False)
    
    def disconnect(self, isOK=True, reason=None):
        """Close the connection.

        Called disconnect instead of close (the usual counterpoint in the socket library)
        because you can reconnect at any time by calling connect.
        
        Inputs:
        - isOK: if True, final state is Disconnected, else Failed
        - reason: a string explaining why, or None to leave unchanged;
            please specify a reason if isOK is false!   
        """
        self._sock.close(isOK=isOK, reason=reason)

    @property
    def fullState(self):
        """Returns the current state as a tuple:
        - state: the state, as a string
        - reason: the reason for the state ("" if none)
        """
        return (self._state, self._reason)

    @property
    def state(self):
        """Returns the current state as a string.
        """
        return self._state
    
    @property
    def isConnected(self):
        """Return True if connected, False otherwise.
        """
        return self._state in self._ConnectedStates
    
    @property
    def isDisconnected(self):
        """Return True if fully disconnected, False otherwise.
        """
        return self._state in self._DisconnectedStates

    @property
    def isDone(self):
        """Return True if the last transition is finished, i.e. connected, disconnected or failed.
        """
        return self._state in self._DoneStates
    
    @property
    def didFail(self):
        """Return True if the connection failed
        """
        return self._state in self._FailedStates
    
    @property
    def mayConnect(self):
        """Return True if one may call connect, false otherwise"""
        return self._state not in (self.Connected, self.Connecting, self.Authorizing)

    def removeReadCallback(self, readCallback):
        """Attempt to remove the read callback function;

        Returns True if successful, False if the subr was not found in the list.
        """
        try:
            self._userReadCallbacks.remove(readCallback)
            return True
        except ValueError:
            return False

    def removeStateCallback(self, stateCallback):
        """Attempt to remove the state callback function;

        Returns True if successful, False if the subr was not found in the list.
        """
        try:
            self._stateCallbacks.remove(stateCallback)
            return True
        except ValueError:
            return False
    
    def write(self, astr):
        """Write data to the socket. Does not block.
        
        Safe to call as soon as you call connect, but of course
        no data is sent until the connection is made.
        
        Raises UnicodeError if the data cannot be expressed as ascii.
        Raises RuntimeError if the socket is not connecting or connected.
        If an error occurs while sending the data, the socket is closed,
        the state is set to Failed and _reason is set.
        """
        self._sock.write(astr)

    def writeLine(self, astr):
        """Send a line of data, appending newline.

        Raises UnicodeError if the data cannot be expressed as ascii.
        Raises RuntimeError if the socket is not connecting or connected.
        If an error occurs while sending the data, the socket is closed,
        the state is set to Failed and _reason is set.
        """
        self._sock.writeLine(astr)
    
    def _authDone(self, msg=""):
        """Call from your authorization callback function
        when authorization succeeds.
        Do not call unless you specified an authorization callback function.
        
        If authorization fails, call self.disconnect(False, error msg) instead.
        """
        self._setRead(forAuth=False)
        self._setState(self.Connected, msg)
    
    def _setRead(self, forAuth=False):
        """Set up reads.
        """
        if (forAuth and self._authReadLines) or (not forAuth and self._readLines):
            self._sock.setReadCallback(self._sockReadLineCallback)
        else:
            self._sock.setReadCallback(self._sockReadCallback)
        if forAuth:
            self._currReadCallbacks = [self._authReadCallback,]
        else:
            self._currReadCallbacks = self._userReadCallbacks

    def _setState(self, newState, reason=None):
        """Set the state and reason. If anything has changed, call the state callback functions.

        Inputs:
        - newState  one of the state constants defined at top of file
        - reason    the reason for the change (a string, or None to leave unchanged)
        """
        #print "_setState(newState=%s, reason=%s); self._stateCallbacks=%s" % (newState, reason, self._stateCallbacks)
        oldStateReason = (self._state, self._reason)
        if newState not in self._AllStates:
            raise RuntimeError("unknown connection state: %s" % (newState,))
        self._state = newState
        if reason != None:
            self._reason = str(reason)
        
        # if the state or reason has changed, call state callbacks
        if oldStateReason != (self._state, self._reason):
            for stateCallback in self._stateCallbacks:
                safeCall2("%s._setState" % (self,), stateCallback, self)
    
    def _sockReadCallback(self, sock):
        """Read callback for the socket in binary mode (not line mode).
                
        When data is received, read it and issues all callbacks.
        """
        dataRead = sock.read()
        #print "%s._sockReadCallback(sock=%r) called; data=%r" % (self, sock, dataRead)
        for subr in self._currReadCallbacks:
            subr(sock, dataRead)

    def _sockReadLineCallback(self, sock):
        """Read callback for the socket in line mode.
                
        Whenever a line is received, issues all callbacks, first stripping the line terminator.
        """
        dataRead = sock.readLine()
        if dataRead is None:
            # only a partial line was available
            return
        #print "%s._sockReadLineCallback(sock=%r) called with data %r" % (self, sock, dataRead)
        for subr in self._currReadCallbacks:
            subr(sock, dataRead)
    
    def _sockStateCallback(self, sock):
        sockState, reason = sock.fullState
        try:
            locState = self._localSocketStateDict[sockState]
        except KeyError:
            sys.stderr.write("unknown TCPSocket state %r\n" % sockState)
            return
        self._setState(locState, reason)

    def _getArgStr(self):
        """Return main arguments as a string, for __str__
        """
        return "name=%r" % (self._name)

    def __str__(self):
        return "%s(%s)" % (self.__class__.__name__, self._getArgStr())