def run(self):
        """ Waits and receives messages from the server. This
            function somewhat needs to block so is executed in its
            own thread until self._request_shutdown is called.
        """
        while not self._request_shutdown:

            # Find out if we have any data pending from the
            # server
            try:

                # If we've been asked to stop running the
                # request loop. We'll just sit and wait
                # till we get asked to run again
                if self._state not in [STATE_AUTHENTICATING,STATE_WEBSOCKET_CONNECTED,STATE_CONNECTED]:
                    self._request_loop_notify_restart.acquire()
                    self._request_loop_notify_restart.wait(self._loop_timeout)
                    self._request_loop_notify_restart.release()
                    continue

                # If we don't have a websocket defined.
                # we don't go further either
                elif not self.ws:
                    logger.debug("No longer have a websocket. Marking disconnected")
                    self._state = STATE_DISCONNECTED
                    continue

                # Okay, we think we're okay so let's try and read some data
                data = self.ws.recv()
            except io.BlockingIOError:
                continue
            except websocket.WebSocketTimeoutException:
                continue
            except websocket.WebSocketConnectionClosedException as ex:
                logger.debug("WebSocketConnectionClosedException: Requesting disconnect:".format(ex))
                self.disconnect()

                # If the server disconnected, let's try and reconnect
                # back to the service after a random few seconds
                if self.auto_reconnect:
                    # As doing a reconnect would block and would then
                    # prevent us from ticking the websoocket, we'll
                    # go into a subthread to deal with the reconnection
                    def reconnect():
                        self.reconnect()
                    t = threading.Thread(target=reconnect)
                    t.start()

                    # FIXME: need to randomly wait
                    time.sleep(1)
                    if not data: continue
            except Exception as ex:
                logger.error("ERROR in main loop: {}".format(ex))
                continue

            try:
                logger.debug("<RCV: {}".format(data))
                message = WampMessage.loads(data)
                logger.debug("<RCV: {}".format(message.dump()))
                try:
                    code_name = message.code_name.lower()
                    handler_name = "handle_"+code_name
                    handler_function = getattr(self,handler_name)
                    handler_function(message)
                except AttributeError as ex:
                    self.handle_unknown(message)
            except Exception as ex:
                logger.error("ERROR in main loop: {}".format(ex))
Waits and receives messages from the server. This
            function somewhat needs to block so is executed in its
            own thread until self._request_shutdown is called.