Create or retrieve a socket of the right type, already connected
        to the address. Address (ip:port) must be fully specified at this
        point. core.address can be used to generate an address.

def get_socket(self, address, role):
        """Create or retrieve a socket of the right type, already connected
        to the address. Address (ip:port) must be fully specified at this
        point. core.address can be used to generate an address.
        """
        #
        # If this thread doesn't yet have a sockets cache
        # in its local storage, create one here.
        #
        try:
            self._tls.sockets
        except AttributeError:
            self._tls.sockets = {}
        
        # Convert the address to a single canonical string.
        #
        # If a list of addresses is passed, turn it into a tuple
        # of canonical addresses for use as a dictionary key. 
        # Otherwise convert it to a single canonical string.
        #
        if isinstance(address, list):
            caddress = tuple(core.address(a) for a in address)
        else:
            caddress = core.address(address)
            
        #
        # Each socket is identified for this thread by its address(es)
        # and the role the socket is playing (listener, publisher, etc.)
        # That is, within one thread, we are cacheing a read or a write
        # socket to the same address(es).
        #
        # The slight corner case from this is that if you attempt to
        # send to [addressA, addressB] and then to addressA and then
        # to [addressB, addressA], three separate sockets will be
        # created and used.
        #
        identifier = caddress
        
        if identifier not in self._tls.sockets:
            _logger.debug("%s does not exist in local sockets", identifier)
            #
            # If this is a listening / subscribing socket, it can only
            # be bound once, regardless of thread. Therefore keep a
            # threads-global list of addresses used and make sure this
            # one hasn't been used elsewhere.
            #
            if role in Socket.binding_roles:
                with self._lock:
                    if identifier in self._sockets:
                        raise core.SocketAlreadyExistsError("You cannot create a listening socket in more than one thread")
                    else:
                        self._sockets.add(identifier)
            
            type = self.roles[role]
            socket = context.socket(type)
            socket.role = role
            socket.address = caddress
            #
            # Do this last so that an exception earlier will result
            # in the socket not being cached
            #
            self._tls.sockets[identifier] = socket
        else:
            _logger.debug("%s already not exist in local sockets", identifier)
            #
            # Only return sockets created in this thread
            #
            socket = self._tls.sockets[identifier]

        return socket