예제 #1
0
 def __init__(self,
              resolved_name,
              recv_data_class,
              queue_size=None,
              buff_size=DEFAULT_BUFF_SIZE):
     """
     ctor
     @param resolved_name: resolved service or topic name
     @type  resolved_name: str
     @param recv_data_class: message class for deserializing inbound messages
     @type  recv_data_class: Class
     @param queue_size: maximum number of inbound messages to maintain
     @type  queue_size: int
     @param buff_size: receive buffer size (in bytes) for reading from the connection.
     @type  buff_size: int
     """
     if recv_data_class and not issubclass(recv_data_class, Message):
         raise TransportInitError(
             "Unable to initialize transport: data class is not a message data class"
         )
     self.resolved_name = resolved_name
     self.recv_data_class = recv_data_class
     self.queue_size = queue_size
     self.buff_size = buff_size
     self.direction = BIDIRECTIONAL
예제 #2
0
    def __init__(self, protocol, name, header=None):
        """
        ctor
        @param name str: identifier
        @param protocol TCPROSTransportProtocol protocol implementation    
        @param header dict: (optional) handshake header if transport handshake header was
        already read off of transport.
        @raise TransportInitError if transport cannot be initialized according to arguments
        """
        super(TCPROSTransport, self).__init__(protocol.direction, name=name)
        if not name:
            raise TransportInitError(
                "Unable to initialize transport: name is not set")

        self.protocol = protocol

        self.socket = None
        self.endpoint_id = 'unknown'
        self.read_buff = cStringIO.StringIO()
        self.write_buff = cStringIO.StringIO()

        self.header = header

        # #1852 have to hold onto latched messages on subscriber side
        self.is_latched = False
        self.latch = None

        # these fields are actually set by the remote
        # publisher/service. they are set for tools that connect
        # without knowing the actual field name
        self.md5sum = None
        self.type = None
예제 #3
0
    def connect(self, dest_addr, dest_port, endpoint_id, timeout=None):
        """
        Establish TCP connection to the specified
        address/port. connect() always calls L{write_header()} and
        L{read_header()} after the connection is made
        @param dest_addr: destination IP address
        @type  dest_addr: str
        @param dest_port: destination port
        @type  dest_port: int                
        @param endpoint_id: string identifier for connection (for statistics)
        @type  endpoint_id: str
        @param timeout: (optional keyword) timeout in seconds
        @type  timeout: float
        @raise TransportInitError: if unable to create connection
        """
        try:
            self.endpoint_id = endpoint_id
            self.dest_address = (dest_addr, dest_port)
            if rosgraph.network.use_ipv6():
                s = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
            else:
                s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            if _is_use_tcp_keepalive():
                # OSX (among others) does not define these options
                if hasattr(socket, 'TCP_KEEPCNT') and \
                   hasattr(socket, 'TCP_KEEPIDLE') and \
                   hasattr(socket, 'TCP_KEEPINTVL'):
                    # turn on KEEPALIVE
                    s.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
                    # - # keepalive failures before actual connection failure
                    s.setsockopt(socket.SOL_TCP, socket.TCP_KEEPCNT, 9)
                    # - timeout before starting KEEPALIVE process
                    s.setsockopt(socket.SOL_TCP, socket.TCP_KEEPIDLE, 60)
                    # - interval to send KEEPALIVE after IDLE timeout
                    s.setsockopt(socket.SOL_TCP, socket.TCP_KEEPINTVL, 10)
            if timeout is not None:
                s.settimeout(timeout)
            self.socket = s
            logdebug('connecting to ' + str(dest_addr) + ' ' + str(dest_port))
            self.socket.connect((dest_addr, dest_port))
            self.write_header()
            self.read_header()
            self.local_endpoint = self.socket.getsockname()
            self.remote_endpoint = (dest_addr, dest_port)
        except TransportInitError as tie:
            rospyerr(
                "Unable to initiate TCP/IP socket to %s:%s (%s): %s" %
                (dest_addr, dest_port, endpoint_id, traceback.format_exc()))
            raise
        except Exception as e:
            #logerr("Unknown error initiating TCP/IP socket to %s:%s (%s): %s"%(dest_addr, dest_port, endpoint_id, str(e)))
            rospywarn(
                "Unknown error initiating TCP/IP socket to %s:%s (%s): %s" %
                (dest_addr, dest_port, endpoint_id, traceback.format_exc()))

            # FATAL: no reconnection as error is unknown
            self.close()
            raise TransportInitError(str(e))  #re-raise i/o error
예제 #4
0
 def _validate_header(self, header):
     """
     Validate header and initialize fields accordingly
     @param header: header fields from publisher
     @type  header: dict
     @raise TransportInitError: if header fails to validate
     """
     self.header = header
     if 'error' in header:
         raise TransportInitError("remote error reported: %s"%header['error'])
     for required in ['md5sum', 'type']:
         if not required in header:
             raise TransportInitError("header missing required field [%s]"%required)
     self.md5sum = header['md5sum']
     self.callerid_pub = header['callerid']
     self.type = header['type']
     if header.get('latching', '0') == '1':
         self.is_latched = True
예제 #5
0
    def _tcp_server_callback(self, sock, client_addr):
        """
        TCPServer callback: detects incoming topic or service connection and passes connection accordingly
    
        @param sock: socket connection
        @type  sock: socket.socket
        @param client_addr: client address
        @type  client_addr: (str, int)
        @raise TransportInitError: If transport cannot be succesfully initialized
        """
        #TODOXXX:rewrite this logic so it is possible to create TCPROSTransport object first, set its protocol,
        #and then use that to do the writing
        try:
            buff_size = 4096  # size of read buffer
            if python3 == 0:
                #initialize read_ros_handshake_header with BytesIO for Python 3 (instead of bytesarray())
                header = read_ros_handshake_header(sock, StringIO(), buff_size)
            else:
                header = read_ros_handshake_header(sock, BytesIO(), buff_size)

            if 'topic' in header:
                err_msg = self.topic_connection_handler(
                    sock, client_addr, header)
            elif 'service' in header:
                err_msg = self.service_connection_handler(
                    sock, client_addr, header)
            else:
                err_msg = 'no topic or service name detected'
            if err_msg:
                # shutdown race condition: nodes that come up and down
                # quickly can receive connections during teardown.

                # We use is_shutdown_requested() because we can get
                # into bad connection states during client shutdown
                # hooks.
                if not rospy.core.is_shutdown_requested():
                    write_ros_handshake_header(sock, {'error': err_msg})
                    raise TransportInitError(
                        "Could not process inbound connection: " + err_msg +
                        str(header))
                else:
                    write_ros_handshake_header(sock,
                                               {'error': 'node shutting down'})
                    return
        except rospy.exceptions.TransportInitError as e:
            logwarn(str(e))
            if sock is not None:
                sock.close()
        except Exception as e:
            # collect stack trace separately in local log file
            if not rospy.core.is_shutdown_requested():
                logwarn("Inbound TCP/IP connection failed from '%s': %s",
                        str(client_addr), e)
                rospyerr("Inbound TCP/IP connection failed from '%s':\n%s",
                         str(client_addr), traceback.format_exc())
            if sock is not None:
                sock.close()
예제 #6
0
 def set_socket(self, sock, endpoint_id):
     """
     Set the socket for this transport
     @param sock: socket
     @type  sock: socket.socket
     @param endpoint_id: identifier for connection endpoint
     @type  endpoint_id: str
     @raise TransportInitError: if socket has already been set
     """
     if self.socket is not None:
         raise TransportInitError("socket already initialized")
     self.socket = sock
     self.endpoint_id = endpoint_id
예제 #7
0
 def write_data(self, data):
     """
     Write raw data to transport
     @raise TransportInitialiationError: could not be initialized
     @raise TransportTerminated: no longer open for publishing
     """
     if not self.socket:
         raise TransportInitError(
             "TCPROS transport was not successfully initialized")
     if self.done:
         raise TransportTerminated("connection closed")
     try:
         #TODO: get rid of sendalls and replace with async-style publishing
         self.socket.sendall(data.encode())
         self.stat_bytes += len(data)
         self.stat_num_msg += 1
     except IOError as xxx_todo_changeme:
         #for now, just document common errno's in code
         (errno, msg) = xxx_todo_changeme.args
         #for now, just document common errno's in code
         if errno == 32:  #broken pipe
             logdebug("ERROR: Broken Pipe")
             self.close()
             raise TransportTerminated(str(errno) + msg)
         raise  #re-raise
     except socket.error as xxx_todo_changeme1:
         #for now, just document common errno's in code
         (errno, msg) = xxx_todo_changeme1.args
         #for now, just document common errno's in code
         if errno == 32:  #broken pipe
             logdebug("[%s]: Closing connection [%s] due to broken pipe",
                      self.name, self.endpoint_id)
             self.close()
             raise TransportTerminated(msg)
         elif errno == 104:  #connection reset by peer
             logdebug("[%s]: Peer [%s] has closed connection", self.name,
                      self.endpoint_id)
             self.close()
             raise TransportTerminated(msg)
         else:
             rospydebug("unknown socket error writing data: %s",
                        traceback.format_exc())
             logdebug(
                 "[%s]: closing connection [%s] due to unknown socket error: %s",
                 self.name, self.endpoint_id, msg)
             self.close()
             raise TransportTerminated(str(errno) + ' ' + msg)
     #except:
     #TODO: try to figure out common errors here
     #raise
     return True
예제 #8
0
    def __init__(self, protocol, name, header=None):
        """
        ctor
        @param name str: identifier
        @param protocol TCPROSTransportProtocol protocol implementation    
        @param header dict: (optional) handshake header if transport handshake header was
        already read off of transport.
        @raise TransportInitError if transport cannot be initialized according to arguments
        """
        super(TCPROSTransport, self).__init__(protocol.direction, name=name)
        if not name:
            raise TransportInitError(
                "Unable to initialize transport: name is not set")

        self.protocol = protocol

        self.socket = None
        self.endpoint_id = 'unknown'
        self.callerid_pub = 'unknown'
        self.dest_address = None  # for reconnection

        if python3 == 0:  # Python 2.x
            self.read_buff = StringIO()
            self.write_buff = StringIO()
        else:  # Python 3.x
            self.read_buff = BytesIO()
            self.write_buff = BytesIO()

        #self.write_buff = StringIO()
        self.header = header

        # #1852 have to hold onto latched messages on subscriber side
        self.is_latched = False
        self.latch = None

        # save the fileno separately so we can garbage collect the
        # socket but still unregister will poll objects
        self._fileno = None

        # these fields are actually set by the remote
        # publisher/service. they are set for tools that connect
        # without knowing the actual field name
        self.md5sum = None
        self.type = None

        # Endpoint Details (IP, Port)
        self.local_endpoint = (None, None)
        self.remote_endpoint = (None, None)
예제 #9
0
 def _tcp_server_callback(self, sock, client_addr):
     """
     TCPServer callback: detects incoming topic or service connection and passes connection accordingly
 
     @param sock: socket connection
     @type  sock: socket.socket
     @param client_addr: client address
     @type  client_addr: (str, int)
     @raise TransportInitError: If transport cannot be succesfully initialized
     """
     #TODOXXX:rewrite this logic so it is possible to create TCPROSTransport object first, set its protocol,
     #and then use that to do the writing
     try:
         buff_size = 4096  # size of read buffer
         header = read_ros_handshake_header(sock, cStringIO.StringIO(),
                                            buff_size)
         if 'topic' in header:
             err_msg = self.topic_connection_handler(
                 sock, client_addr, header)
         elif 'service' in header:
             err_msg = self.service_connection_handler(
                 sock, client_addr, header)
         else:
             err_msg = 'no topic or service name detected'
         if err_msg:
             # shutdown race condition: nodes that come up and down quickly can receive connections during teardown
             if not rospy.core.is_shutdown():
                 write_ros_handshake_header(sock, {'error': err_msg})
                 raise TransportInitError(
                     "Could not process inbound connection: " + err_msg +
                     str(header))
             else:
                 write_ros_handshake_header(sock,
                                            {'error': 'node shutting down'})
                 return
     except rospy.exceptions.TransportInitError, e:
         logwarn(str(e))
         if sock is not None:
             sock.close()
예제 #10
0
 def write_data(self, data):
     """
     Write raw data to transport
     @raise TransportInitialiationError: could not be initialized
     @raise TransportTerminated: no longer open for publishing
     """
     if not self.socket:
         raise TransportInitError(
             "TCPROS transport was not successfully initialized")
     if self.done:
         raise TransportTerminated("connection closed")
     try:
         #TODO: get rid of sendalls and replace with async-style publishing
         self.socket.sendall(data)
         self.stat_bytes += len(data)
         self.stat_num_msg += 1
     except IOError, (errno, msg):
         #for now, just document common errno's in code
         if errno == 32:  #broken pipe
             logdebug("ERROR: Broken Pipe")
             self.close()
             raise TransportTerminated(str(errno) + msg)
         raise  #re-raise
예제 #11
0
    def connect(self, dest_addr, dest_port, endpoint_id, timeout=None):
        """
        Establish TCP connection to the specified
        address/port. connect() always calls L{write_header()} and
        L{read_header()} after the connection is made
        @param dest_addr: destination IP address
        @type  dest_addr: str
        @param dest_port: destination port
        @type  dest_port: int                
        @param endpoint_id: string identifier for connection (for statistics)
        @type  endpoint_id: str
        @param timeout: (optional keyword) timeout in seconds
        @type  timeout: float
        @raise TransportInitError: if unable to create connection
        """
        # first make sure that if ROS_HOSTNAME=localhost, we will not attempt
        # to connect to anything other than localhost
        if ("ROS_HOSTNAME" in os.environ) and (os.environ["ROS_HOSTNAME"] == "localhost"):
          if not rosgraph.network.is_local_address(dest_addr):
            msg = "attempted to connect to non-local host [%s] from a node launched with ROS_HOSTNAME=localhost" % (dest_addr)
            logwarn(msg)
            self.close()
            raise TransportInitError(msg)  # bubble up
 
        # now we can proceed with trying to connect.
        try:
            self.endpoint_id = endpoint_id
            self.dest_address = (dest_addr, dest_port)
            if rosgraph.network.use_ipv6():
                s = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
            else:
                s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            if _is_use_tcp_keepalive():
                # OSX (among others) does not define these options
                if hasattr(socket, 'TCP_KEEPCNT') and \
                   hasattr(socket, 'TCP_KEEPIDLE') and \
                   hasattr(socket, 'TCP_KEEPINTVL'):
                    # turn on KEEPALIVE
                    s.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
                    # - # keepalive failures before actual connection failure
                    s.setsockopt(socket.SOL_TCP, socket.TCP_KEEPCNT, 9)
                    # - timeout before starting KEEPALIVE process
                    s.setsockopt(socket.SOL_TCP, socket.TCP_KEEPIDLE, 60)
                    # - interval to send KEEPALIVE after IDLE timeout
                    s.setsockopt(socket.SOL_TCP, socket.TCP_KEEPINTVL, 10)
            if timeout is not None:
                s.settimeout(timeout)
            self.socket = s
            logdebug('connecting to ' + str(dest_addr)+ ' ' + str(dest_port))
            self.socket.connect((dest_addr, dest_port))
            self.write_header()
            self.read_header()
            self.local_endpoint = self.socket.getsockname()
            self.remote_endpoint = (dest_addr, dest_port)
        except TransportInitError as tie:
            rospyerr("Unable to initiate TCP/IP socket to %s:%s (%s): %s"%(dest_addr, dest_port, endpoint_id, traceback.format_exc()))            
            raise
        except Exception as e:
            #logerr("Unknown error initiating TCP/IP socket to %s:%s (%s): %s"%(dest_addr, dest_port, endpoint_id, str(e)))
            rospywarn("Unknown error initiating TCP/IP socket to %s:%s (%s): %s"%(dest_addr, dest_port, endpoint_id, traceback.format_exc()))
            # check for error type and reason. On unknown errors the socket will be closed
            # to avoid reconnection and error reproduction
            if not isinstance(e, socket.error):
                # FATAL: no reconnection as error is unknown
                self.close()
            elif not isinstance(e, socket.timeout) and e.errno not in [
                    errno.ENETDOWN, errno.ENETUNREACH, errno.ENETRESET,
                    errno.ECONNABORTED, errno.ETIMEDOUT, errno.EHOSTDOWN, errno.EHOSTUNREACH]:
                # reconnect in follow cases, otherwise close the socket:
                # 1. socket.timeout: on timeouts caused by delays on wireless links
                # 2. ENETDOWN (100), ENETUNREACH (101), ENETRESET (102), ECONNABORTED (103):
                #     while using ROS_HOSTNAME ros binds to a specific interface. Theses errors
                #     are thrown on interface shutdown e.g. on reconnection in LTE networks
                # 3. ETIMEDOUT (110): same like 1. (for completeness)
                # 4. EHOSTDOWN (112), EHOSTUNREACH (113): while network and/or DNS-server is not reachable
                #
                # no reconnection as error is not 1.-4.
                self.close()
            raise TransportInitError(str(e)) #re-raise i/o error
예제 #12
0
class TCPROSTransport(Transport):
    """
    Generic implementation of TCPROS exchange routines for both topics and services
    """
    transport_type = 'TCPROS'

    def __init__(self, protocol, name, header=None):
        """
        ctor
        @param name str: identifier
        @param protocol TCPROSTransportProtocol protocol implementation    
        @param header dict: (optional) handshake header if transport handshake header was
        already read off of transport.
        @raise TransportInitError if transport cannot be initialized according to arguments
        """
        super(TCPROSTransport, self).__init__(protocol.direction, name=name)
        if not name:
            raise TransportInitError(
                "Unable to initialize transport: name is not set")

        self.protocol = protocol

        self.socket = None
        self.endpoint_id = 'unknown'
        self.read_buff = cStringIO.StringIO()
        self.write_buff = cStringIO.StringIO()

        self.header = header

        # #1852 have to hold onto latched messages on subscriber side
        self.is_latched = False
        self.latch = None

        # these fields are actually set by the remote
        # publisher/service. they are set for tools that connect
        # without knowing the actual field name
        self.md5sum = None
        self.type = None

    def set_socket(self, sock, endpoint_id):
        """
        Set the socket for this transport
        @param sock: socket
        @type  sock: socket.socket
        @param endpoint_id: identifier for connection endpoint
        @type  endpoint_id: str
        @raise TransportInitError: if socket has already been set
        """
        if self.socket is not None:
            raise TransportInitError("socket already initialized")
        self.socket = sock
        self.endpoint_id = endpoint_id

    def connect(self, dest_addr, dest_port, endpoint_id, timeout=None):
        """
        Establish TCP connection to the specified
        address/port. connect() always calls L{write_header()} and
        L{read_header()} after the connection is made
        @param dest_addr: destination IP address
        @type  dest_addr: str
        @param dest_port: destination port
        @type  dest_port: int                
        @param endpoint_id: string identifier for connection (for statistics)
        @type  endpoint_id: str
        @param timeout: (optional keyword) timeout in seconds
        @type  timeout: float
        @raise TransportInitError: if unable to create connection
        """
        try:
            self.endpoint_id = endpoint_id

            s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            if _is_use_tcp_keepalive():
                # OSX (among others) does not define these options
                if hasattr(socket, 'TCP_KEEPCNT') and \
                   hasattr(socket, 'TCP_KEEPIDLE') and \
                   hasattr(socket, 'TCP_KEEPINTVL'):
                    # turn on KEEPALIVE
                    s.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
                    # - # keepalive failures before actual connection failure
                    s.setsockopt(socket.SOL_TCP, socket.TCP_KEEPCNT, 9)
                    # - timeout before starting KEEPALIVE process
                    s.setsockopt(socket.SOL_TCP, socket.TCP_KEEPIDLE, 60)
                    # - interval to send KEEPALIVE after IDLE timeout
                    s.setsockopt(socket.SOL_TCP, socket.TCP_KEEPINTVL, 10)
            if timeout is not None:
                s.settimeout(timeout)
            self.socket = s
            self.socket.connect((dest_addr, dest_port))
            self.write_header()
            self.read_header()
        except TransportInitError, tie:
            rospyerr(
                "Unable to initiate TCP/IP socket to %s:%s (%s): %s" %
                (dest_addr, dest_port, endpoint_id, traceback.format_exc()))
            raise
        except Exception, e:
            self.done = True
            if self.socket:
                self.socket.close()
            self.socket = None

            #logerr("Unknown error initiating TCP/IP socket to %s:%s (%s): %s"%(dest_addr, dest_port, endpoint_id, str(e)))
            rospywarn(
                "Unknown error initiating TCP/IP socket to %s:%s (%s): %s" %
                (dest_addr, dest_port, endpoint_id, traceback.format_exc()))
            raise TransportInitError(str(e))  #re-raise i/o error