コード例 #1
0
    def start(self):
        # Create server
        self.rpc_server = ThreadedXMLRPCServer((self.host, self.port),
                                               requestHandler=RequestHandler,
                                               logRequests=False)
        self.rpc_server.register_introspection_functions()

        # Register RPC functions.
        self.rpc_server.register_function(self.forwardMessage)
        self.rpc_server.register_function(self.registerNewPeer)
        self.rpc_server.register_function(self.leavePeer)

        server_thread = threading.Thread(name="server", target=self._server)
        server_thread.setDaemon(True)  # Don't wait for server thread to exit.
        server_thread.start()

        queue_thread = threading.Thread(name="queue",
                                        target=self._queue_handler)
        queue_thread.setDaemon(True)  # Don't wait for server thread to exit.
        queue_thread.start()
コード例 #2
0
ファイル: Peer.py プロジェクト: bufas/headphoneparty
    def start(self):
        if self.register:
            s = ServerProxy('http://' + self.routerHost + ':' + str(self.routerPort))
            s.registerNewPeer(self.name, self.host, self.port)

        # Create server
        self.rpc_server = ThreadedXMLRPCServer((self.host, self.port), requestHandler=RequestHandler, logRequests=False)
        self.rpc_server.register_introspection_functions()

        # Register RPC functions.
        self.rpc_server.register_function(self.ReceiveMsg)

        server_thread = threading.Thread(name="server", target=self._server)
        server_thread.setDaemon(True)  # Don't wait for server thread to exit.
        server_thread.start()

        if not self.manualOverride:
            out_of_range_check_thread = threading.Thread(name="out_of_range_check_thread", target=self._check_out_of_range)
            out_of_range_check_thread.setDaemon(True)  # Don't wait for server thread to exit.
            out_of_range_check_thread.start()

        print("ready")

        self._main_loop()
コード例 #3
0
ファイル: Router.py プロジェクト: bufas/headphoneparty
    def start(self):
        # Create server
        self.rpc_server = ThreadedXMLRPCServer((self.host, self.port), requestHandler=RequestHandler, logRequests=False)
        self.rpc_server.register_introspection_functions()

        # Register RPC functions.
        self.rpc_server.register_function(self.forwardMessage)
        self.rpc_server.register_function(self.registerNewPeer)
        self.rpc_server.register_function(self.leavePeer)

        server_thread = threading.Thread(name="server", target=self._server)
        server_thread.setDaemon(True)  # Don't wait for server thread to exit.
        server_thread.start()

        queue_thread = threading.Thread(name="queue", target=self._queue_handler)
        queue_thread.setDaemon(True)  # Don't wait for server thread to exit.
        queue_thread.start()
コード例 #4
0
ファイル: Peer.py プロジェクト: bufas/headphoneparty
class Peer(object):
    def __init__(self, register, name, host, port, routerHost, routerPort, manualOverride, clockSyncActive):
        self.name, self.host, self.port = name, host, port
        self.routerHost, self.routerPort = routerHost, routerPort
        self.manualOverride, self.clockSyncActive = manualOverride, clockSyncActive
        self.register = register

        # [{'song_name': 'Britney Spears - Toxic', 'votes': [{'peer_name': 'P1', 'sig': 'signature_on_song', 'pk': ..., 'pksign': ...}, {'peer_name': 'P2', 'sig': '...', ...}]]
        self.playlist = []
        self.playlistLock = RLock()
        self.songplaying = None
        self.firstJoin = True

        self.progressLock = Lock()
        self.quitting = False


        keydist = KeyDistributer()
        self.key = keydist.getKeyPair()

        self.msg_count = 0
        self.MSG_IDS_SEEN_MAXSIZE = 1000
        self.msg_ids_seen = [-1] * self.MSG_IDS_SEEN_MAXSIZE
        self.msg_ids_seen_nextindex = 0
        self.msg_ids_seen_lock = RLock()

        self.playlist_request_id = None
        self.playlists_received = 0

        self.hasJoined = False

        self._time_since_last_msg = 0
        self._time_since_last_msg_lock = Lock()

        self._top = [None] * LOCK_TOP   # [('song', 42 votes), (...)]
        self._toplock = RLock()

        self.clock = Clock(self, sync=self.clockSyncActive)


    def start(self):
        if self.register:
            s = ServerProxy('http://' + self.routerHost + ':' + str(self.routerPort))
            s.registerNewPeer(self.name, self.host, self.port)

        # Create server
        self.rpc_server = ThreadedXMLRPCServer((self.host, self.port), requestHandler=RequestHandler, logRequests=False)
        self.rpc_server.register_introspection_functions()

        # Register RPC functions.
        self.rpc_server.register_function(self.ReceiveMsg)

        server_thread = threading.Thread(name="server", target=self._server)
        server_thread.setDaemon(True)  # Don't wait for server thread to exit.
        server_thread.start()

        if not self.manualOverride:
            out_of_range_check_thread = threading.Thread(name="out_of_range_check_thread", target=self._check_out_of_range)
            out_of_range_check_thread.setDaemon(True)  # Don't wait for server thread to exit.
            out_of_range_check_thread.start()

        print("ready")

        self._main_loop()

    def _check_out_of_range(self):
        while True:
            time.sleep(OUT_OF_RANGE_CHECK_INTERVAL)
            if self.hasJoined:
                with self._time_since_last_msg_lock:
                    self._time_since_last_msg += OUT_OF_RANGE_CHECK_INTERVAL
                    if self._time_since_last_msg > OUT_OF_RANGE_TIME_LIMIT:
                        self.hasJoined = False
                        self._join()
                        self._time_since_last_msg = 0


    def _join(self):
        if not self.firstJoin:
            with self._toplock:
                for topitem in self._top:
                    if topitem:
                        (song_name, _) = topitem
                        self._shout_votes(song_name)
        self.firstJoin = False
        self.playlists_received = 0
        self.playlist_request_id = self._gen_msg_id()
        self._send_msg("GETLIST", {'request_id': self.playlist_request_id})
        if not self.manualOverride:
            # Start join timeout
            join_timeout_thread = threading.Thread(name="join", target=self._join_timeout)
            join_timeout_thread.setDaemon(True)  # Don't wait for thread to exit.
            join_timeout_thread.start()


    def _join_timeout(self):
        time.sleep(PLAYLIST_RECV_TIMEOUT_ON_JOIN)
        if not self.hasJoined:
            print("JOIN RETRY")
            self._join() # Retry

    def ReceiveMsg(self, msg_id, sender_peer_name, msgtype, argdict):
        if not self.quitting:
            recv_handler_thread = threading.Thread(name="recvhandler", target=self._handle_recv_message,
                                                   args=[msg_id, sender_peer_name, msgtype, argdict])
            recv_handler_thread.start()            
        return ''

    def _handle_recv_message(self, msg_id, sender_peer_name, msgtype, argdict):
        # For some reason argdict values has turned into lists
        txt = self.name + ": GOTMSG of type " + msgtype + " from peer " + sender_peer_name + ": " + str(argdict)
        #logging.debug(txt)
        if self._shouldDropMsg(msg_id):
            print("DROPPED MSG")
        else:
            print(txt)
            if msgtype == "VOTE":
                #logging.debug(self.name + "HANDLE VOTE")
                self._forward_msg(msg_id, sender_peer_name, msgtype, argdict) # Forward
                self._handleVote(argdict['song_name'],
                                 argdict['vote'])
            elif msgtype == "VOTES":
                #logging.debug("GOT VOTESLIST of " + str(argdict['song_name']))
                print("GOT VOTESLIST of " + str(argdict['song_name']))
                self._handleVotes(str(argdict['song_name']),
                                  argdict['votes'])
            elif msgtype == "GETLIST":
                self._send_playlist(argdict['request_id'])
            elif msgtype == "PLAYLIST":
                print("GOT PLAYLIST")
                self._handle_playlist(sender_peer_name,
                                      argdict['request_id'],
                                      argdict['playlist'])
            elif msgtype == "CLOCKSYNC":
                self.clock.recv(int(argdict['t']),
                                int(argdict['s']),
                                sender_peer_name)
        return ''

    def _play_next(self):
        with self.playlistLock:
            with self._toplock:
                if not self._top[0]:
                    print("NOTHING TO PLAY")
                else:
                    (nextsong, _) = self._top[0]

                    # Clean up
                    top3songs = []
                    for i in range(0, LOCK_TOP-1):
                        self._top[i] = self._top[i+1]
                        if self._top[i]:
                            (song, _) = self._top[i]
                            top3songs.append(song)
                    self._top[LOCK_TOP-1] = None

                    # Remove played song
                    for playlistitem in self.playlist:
                        if playlistitem['song_name'] == nextsong:
                            self.playlist.remove(playlistitem)
                            break

                    # Add new song to top
                    maxcnt = 0
                    maxsong = None
                    for playlistitem in self.playlist:
                        if not playlistitem['song_name'] in top3songs:
                            if self._compare_songs((playlistitem['song_name'], len(playlistitem['votes'])), (maxsong, maxcnt)):
                                maxcnt = len(playlistitem['votes'])
                                maxsong = playlistitem['song_name']
                    if maxsong:
                        self._flush_top(maxsong, maxcnt)

                    # Tell
                    playtxt = "PLAYING " + nextsong
                    logging.debug(playtxt)
                    self.songplaying = nextsong
                    logging.debug(str(self._top))
                    print(playtxt)
                    print("TOP " + str(self._top))


    def _addMsgId(self, msg_id):
        with self.msg_ids_seen_lock:
            self.msg_ids_seen[self.msg_ids_seen_nextindex] = msg_id
            self.msg_ids_seen_nextindex += 1
            if self.msg_ids_seen_nextindex >= self.MSG_IDS_SEEN_MAXSIZE:
                self.msg_ids_seen_nextindex = 0

    def _shouldDropMsg(self, msg_id):
        with self.msg_ids_seen_lock:
            if msg_id in self.msg_ids_seen:
                return True
            self._addMsgId(msg_id)
            return False

    def _shout_votes(self, songName):
        print("SHOUTING VOTES")
        for playlistitem in self.playlist:
            if playlistitem['song_name'] == songName:
                self._send_msg("VOTES", {'song_name': songName, 'votes': playlistitem['votes']})
                break

    def _send_playlist(self, request_id):
        params = {'request_id': request_id,
                  'playlist': self.playlist}
        self._send_msg("PLAYLIST", params)

    def _handle_playlist(self, sender_peer_name, request_id, playlist):
        #TODO: Improve verification of playlist
        if self.playlist_request_id:
            if self._verifyPlaylist(playlist):
                if self.playlist_request_id == request_id:
                    #logging.debug("A")
                    self._updatePlaylist(playlist)
                    #logging.debug("B")
                    self.playlists_received += 1
                    if self.playlists_received > NR_PLAYLISTS_PREFERRED_ON_JOIN:
                        self.hasJoined = True
                        self.playlist_request_id = None

    def _handleVote(self, songName, vote):
        with self.playlistLock:
            if songName != self.songplaying: # Avoid same song being added immediatly again due to syncing
                print("##################HANDLING VOTE###############")
                if self._verifyPK(vote['pk'], vote['pksign']) and self._verifyVote(songName, vote):
                    self._addVote(songName, vote)
                else:
                    print('VOTE REJECTED')
                #logging.debug(self.name + "HANDLE VOTE from " + vote['peer_name'])

    def _handleVotes(self, songName, votes):
        with self.playlistLock:
            if songName != self.songplaying: # Avoid same song being added immediatly again due to syncing
            # Merge votes
                for vote in votes:
                    self._addVote(songName, vote)



    def _flush_top(self, updated_song, new_vote_cnt):
        with self._toplock:
            inTop = False
            updated = False

            for i in range(0, LOCK_TOP):
                if self._top[i]:
                    (songX, votecntX) = self._top[i]
                    if updated_song == songX:
                        inTop = True
                        self._top[i] = (songX, new_vote_cnt)
                        if new_vote_cnt != votecntX:
                            updated = True
                        break
            if not inTop:
                if not self._top[LOCK_TOP-1]: # If not specified yet
                    #Find first empty position
                    for i in range(0,LOCK_TOP):
                        if not self._top[i]:
                            self._top[i] = (updated_song, new_vote_cnt)
                            updated = True
                            break
                else:
                    lastTopSongDesc = self._top[LOCK_TOP-1]
                    if self._compare_songs((updated_song, new_vote_cnt), lastTopSongDesc):
                        updated = True
                        self._top[LOCK_TOP -1] = (updated_song, new_vote_cnt)

            # Update internally
            if updated:
                for i in range(LOCK_TOP-1, 0, -1):
                    songdescX = self._top[i]
                    songdescY = self._top[i-1]
                    if self._compare_songs(songdescX, songdescY):
                        self._top[i] = songdescY
                        self._top[i-1] = songdescX
            if updated:
                # Flush votes for updated or new song in top X (vote sync)
                self._shout_votes(updated_song)

    def _compare_songs(self, songdesc1, songdesc2):
        # Returns true if first song has higher rank
        if not songdesc2:
            return True
        if not songdesc1:
            return False
        (song1, votecnt1) = songdesc1
        (song2, votecnt2) = songdesc2
        if votecnt1 > votecnt2 or (votecnt1 == votecnt2 and song1 < song2):
            return True
        return False

    def _addVote(self, songName, vote):
        with self.playlistLock:
            if songName != self.songplaying:
                # If exists
                added = False

                for playlistitem in self.playlist:
                    if playlistitem['song_name'] == songName:
                        if not vote['peer_name'] in [existingVote['peer_name'] for existingVote in playlistitem['votes']]:
                            # The vote is not in the list, add it
                            playlistitem['votes'].append(vote)
                            self._flush_top(songName, len(playlistitem['votes']))
                        added = True
                        break
                if not added:
                    self.playlist.append({'song_name': songName, 'votes': [vote]})
                    self._flush_top(songName, 1)

                print("VOTE ADDED")

    def _updatePlaylist(self, recievedPlaylist):
        for song in recievedPlaylist:
            # Authenticate votes
            for vote in song['votes']:
                self._addVote(song['song_name'], vote)

    def _sign(self, obj):
        return self.key.signMessage(obj)

    def _hashOfPlaylist(self, playlist):
        result = SHA256.new()
        for songDescriptor in playlist:
            result.update(songDescriptor['song_name'].encode())
            for vote in songDescriptor['votes']:
                result.update(vote['peer_name'].encode())
                result.update(vote['sig'].encode())
                result.update(vote['pk'])
                result.update(vote['pksign'].encode())
        return result.digest()

    def _verifyPK(self, pk, pksign):
        return self.key.verifyPublicKey(pk, int(pksign))

    def _verifyVote(self, songName, vote):
        return self.key.verifyMessage(vote['pk'], songName + vote['peer_name'], vote['sig']) and self._verifyPK(vote['pk'], vote['pksign'])

    def _verifyPlaylist(self, playlist):
        # Run through all votes for all songs and verify them
        for songDescripter in playlist:
            for vote in songDescripter['votes']:
                if not self._verifyVote(songDescripter['song_name'], vote):
                    return False
        return True

    def _createVote(self, songName):
        """Creates a vote for a specific song"""
        return {'peer_name': self.name,
                'sig': str(self.key.signMessage(songName + self.name)),
                'pk': self.key.getPublicKey(),
                'pksign': self.key.getPksign()}


    def _forward_msg(self, msg_id, sender_peer_name, msgtype, argdict):
        thread = threading.Thread(name="forward", target=self._do_send_msg,
                                  args=[msg_id, sender_peer_name, msgtype, argdict])
        thread.start()
        return ''

    def _gen_msg_id(self):
        dt = datetime.now()
        self.msg_count += 1
        msg_id = self.name + "_" + str(dt.microsecond) + str(self.msg_count)
        self._addMsgId(msg_id)
        return msg_id

    def _send_msg(self, msgtype, argdict):
        if not self.quitting:
            thread = threading.Thread(name="forward", target=self._do_send_msg,
                                      args=[self._gen_msg_id(), self.name, msgtype, argdict])
            thread.start()
        return ''

    def _do_send_msg(self, msg_id, sender_peer_name, msgtype, argdict):
        s = ServerProxy('http://' + self.routerHost + ':' + str(self.routerPort))
        s.forwardMessage(self.name, msg_id, sender_peer_name, msgtype, argdict)

    def _sendVote(self, songName, vote):
        params = {'song_name': songName,
                  'vote': vote}
        self._send_msg("VOTE", params)

    def _print_songlist(self, prefix, songlist):
        songlist_str = prefix + "#####"
        for songlistitem in songlist:
            songlist_str += songlistitem['song_name'] + "###"
            for vote in songlistitem['votes']:
                first = True
                for key in vote.keys():
                    if not first:
                        songlist_str += "#"
                    else:
                        first = False
                    songlist_str += key + "#" + vote[key].replace("\n", "#LINEBREAK#")
                songlist_str += "@@"
            songlist_str += "####"
        print(songlist_str)

    def _main_loop(self):
        while True:

            cmd = sys.stdin.readline().strip()
            logging.debug(self.name + ": Read command: %s" % cmd)
            if "q" == cmd:
                with self.progressLock:
                    self.quitting = True
                    break
            match = re.match(r'sendmsg (\S+)', cmd)
            if match:
                msg = match.group(1)
                self._send_msg("TXTMSG", {'msg': msg})
                continue
            match = re.match(r'vote (\S+)', cmd)
            if match:
                songName = match.group(1)
                vote = self._createVote(songName)
                self._addVote(songName, vote)
                self._sendVote(songName, vote)
                print("VOTEOK")
                continue
            if "join" == cmd:
                self._join()
                continue
            if "play_next" == cmd:
                self._play_next()
                continue
            if "say_quit" == cmd:
                print("QUITTING")
                continue
            if "get_playlist" == cmd:
                with self.playlistLock:
                    self._print_songlist("PLAYLIST", self.playlist)
                continue
            if "get_top3songs" == cmd:
                with self._toplock:
                    top3str = "TOP3SONGS###"
                    for top3item in self._top:
                        if top3item:
                            (song, votecnt) = top3item
                            top3str += song + "#" + str(votecnt) + "##"
                    print(top3str)
                continue
            if "test_create_fake_vote" == cmd:
                songName = 'Justin Beaver'
                fakeVote = {'peer_name': self.name,
                            'sig': str(self.key.signMessage(songName)),
                            'pk': self.key.getPublicKey(),
                            'pksign': '0'}
                self._sendVote(songName, fakeVote)
                continue
            if "get_logical_clock" == cmd:
                logical = self.clock.getLogical()
                logging.debug(self.name + ' LOGICAL CLOCK ' + str(logical))
                print("LOGICALCLOCK#" + str(logical))
                continue
            if "leave" == cmd:
                s = ServerProxy('http://' + self.routerHost + ':' + str(self.routerPort))
                s.leavePeer(self.name)
                break
            print("Unknown command:", cmd)

    def _server(self):
        logging.debug('Starting peer on: %s:%s' % (self.host, self.port))
        try:
            self.rpc_server.serve_forever()
        finally:
            self.rpc_server.close()
コード例 #5
0
ファイル: Router.py プロジェクト: bufas/headphoneparty
class Router:
    def __init__(self, host, port, peers, peer_controller, useTicks):
        self.host = host
        self.port = port
        self.peer_controller = peer_controller
        self.peers = peers
        self.msg_queue = []
        self.msg_lock = Lock()
        self.msg_cond = Condition(self.msg_lock)
        self.useTicks = useTicks
        self.peer_can_send = {}
        self.queue_was_empty = False
        self.queue_active = False
        self.statlock = Lock()

        self.msgshandled = 0
        self.msgsize = 0
        self.msgtypecnt = {}

        # Init for ticks
        for peer in self.peers:
            self.peer_can_send[peer.name] = 0

    def stats(self):
        msgtypes = ""
        for (msgtype, cnt) in self.msgtypecnt.items():
            msgtypes += msgtype + ": " + str(cnt) + "\n"
        return "TOTAL MESSAGES: " + str(self.msgshandled) + "\n" + \
               "DATA SIZE: " + str(self.msgsize) + "\n" + \
               "--MSG TYPES-- \n" + \
               msgtypes + "\n------------\n"

    def get_msgcnt(self):
        return self.msgshandled
    
    def get_msgsize(self):
        return self.msgsize
    
    def get_msgtypecnt(self):
        return self.msgtypecnt


    def activate_queue(self):
        with self.msg_lock:
            self.queue_active = True
            self.msg_cond.notify()

    def tick(self, num_msgs):
        """Each peer can only send a fixed number of messages each time interval"""
        with self.msg_lock:
            for peer in self.peers:
                self.peer_can_send[peer.name] = num_msgs
            self.msg_cond.notify()

    def wait_queue_empty(self):
        # Check twice with small interval
        while True:
            with self.msg_lock:
                if len(self.msg_queue) > 0:
                    self.queue_was_empty = True
                else:
                    if self.queue_was_empty:
                        self.queue_was_empty = False
                        break
                    else:
                        self.queue_was_empty = True
            time.sleep(0.1)

    def start(self):
        # Create server
        self.rpc_server = ThreadedXMLRPCServer((self.host, self.port), requestHandler=RequestHandler, logRequests=False)
        self.rpc_server.register_introspection_functions()

        # Register RPC functions.
        self.rpc_server.register_function(self.forwardMessage)
        self.rpc_server.register_function(self.registerNewPeer)
        self.rpc_server.register_function(self.leavePeer)

        server_thread = threading.Thread(name="server", target=self._server)
        server_thread.setDaemon(True)  # Don't wait for server thread to exit.
        server_thread.start()

        queue_thread = threading.Thread(name="queue", target=self._queue_handler)
        queue_thread.setDaemon(True)  # Don't wait for server thread to exit.
        queue_thread.start()

    def shutdown(self):
        self.rpc_server.server_close()

    def registerNewPeer(self, name, host, port):
        peer = BasicPeerHandler(name, host, port)
        peer.setLocation(self.peer_controller.generateNewPeerLocation())
        peer.setPeerController(self.peer_controller)
        self.peer_controller.addPeer(peer)
        print("Peer " + name + " joined")
        return ''

    def leavePeer(self, name):
        for peer in self.peers:
            if peer.name == name:
                self.peer_controller.removePeer(peer)
                print("Peer " + name + " left")
                break
        return ''

    # Peer name is the origin of the message
    def forwardMessage(self, immediate_sender, msg_id, peer_name, msgtype, argdict):
        with self.statlock:
            self.msgshandled += 1
            self.msgsize += sys.getsizeof(argdict)
            if not msgtype in self.msgtypecnt:
                self.msgtypecnt[msgtype] = 0
            self.msgtypecnt[msgtype] += 1
        #Find peer
        #TODO: IMPROVE
        immediate_sender_peer = None
        for peer in self.peers:
            if peer.name == immediate_sender:
                immediate_sender_peer = peer
                break
        self.msg_lock.acquire()
        self.msg_queue.append((immediate_sender_peer, msg_id, peer_name, msgtype, argdict))
        self.queue_was_empty = False
        self.msg_cond.notify()
        self.msg_lock.release()
        return ''

    def _server(self):
        print('Starting router on: %s:%s' % (self.host, self.port))
        try:
            self.rpc_server.serve_forever()
        except ValueError:  # Thrown upon exit :(
            pass


    def _queue_handler(self):
        while True:
            with self.msg_lock:
                if not self.queue_active:
                    self.msg_cond.wait()
                    continue
                if len(self.msg_queue) == 0:
                    self.msg_cond.wait()
                for i in range(len(self.msg_queue)):
                    (immediate_sender_peer, msg_id, peer_name, msgtype, argdict) = self.msg_queue[i]
                    if not self.useTicks or (self.peer_can_send[immediate_sender_peer.name]
                                             and self.peer_can_send[immediate_sender_peer.name] > 0):
                        self.msg_queue.pop(i)
                        if self.useTicks:
                            self.peer_can_send[immediate_sender_peer.name] -= 1
                        self._do_forward_msg(immediate_sender_peer, msg_id, peer_name, msgtype, argdict)
                        break

    def _do_forward_msg(self, immediate_sender_peer, msg_id, peer_name, msgtype, argdict):
        peersInRange = self.peer_controller.findPeersInRange(immediate_sender_peer)
        for peer in peersInRange:
            if peer != immediate_sender_peer:
                peer.sendMessage(immediate_sender_peer, msg_id, peer_name, str(msgtype), argdict)

            


    def _main_loop(self):
        while True:
            cmd = sys.stdin.readline().strip()
            if "q" == cmd:
                break
            if "setLocation " in cmd:
                txt = cmd.replace("setLocation ", "").strip()
                loc = txt.split(" ")
                for p in self.peers:
                    if p.name == loc[0]:
                        p.setLocation((int(loc[1]), int(loc[2]), int(loc[3]), int(loc[4])))
                        break
                continue
            if "movePeers" == cmd:
                self.peer_controller.movePeers()
                continue
            print("Unknown command:", cmd)        
コード例 #6
0
class Router:
    def __init__(self, host, port, peers, peer_controller, useTicks):
        self.host = host
        self.port = port
        self.peer_controller = peer_controller
        self.peers = peers
        self.msg_queue = []
        self.msg_lock = Lock()
        self.msg_cond = Condition(self.msg_lock)
        self.useTicks = useTicks
        self.peer_can_send = {}
        self.queue_was_empty = False
        self.queue_active = False
        self.statlock = Lock()

        self.msgshandled = 0
        self.msgsize = 0
        self.msgtypecnt = {}

        # Init for ticks
        for peer in self.peers:
            self.peer_can_send[peer.name] = 0

    def stats(self):
        msgtypes = ""
        for (msgtype, cnt) in self.msgtypecnt.items():
            msgtypes += msgtype + ": " + str(cnt) + "\n"
        return "TOTAL MESSAGES: " + str(self.msgshandled) + "\n" + \
               "DATA SIZE: " + str(self.msgsize) + "\n" + \
               "--MSG TYPES-- \n" + \
               msgtypes + "\n------------\n"

    def get_msgcnt(self):
        return self.msgshandled

    def get_msgsize(self):
        return self.msgsize

    def get_msgtypecnt(self):
        return self.msgtypecnt

    def activate_queue(self):
        with self.msg_lock:
            self.queue_active = True
            self.msg_cond.notify()

    def tick(self, num_msgs):
        """Each peer can only send a fixed number of messages each time interval"""
        with self.msg_lock:
            for peer in self.peers:
                self.peer_can_send[peer.name] = num_msgs
            self.msg_cond.notify()

    def wait_queue_empty(self):
        # Check twice with small interval
        while True:
            with self.msg_lock:
                if len(self.msg_queue) > 0:
                    self.queue_was_empty = True
                else:
                    if self.queue_was_empty:
                        self.queue_was_empty = False
                        break
                    else:
                        self.queue_was_empty = True
            time.sleep(0.1)

    def start(self):
        # Create server
        self.rpc_server = ThreadedXMLRPCServer((self.host, self.port),
                                               requestHandler=RequestHandler,
                                               logRequests=False)
        self.rpc_server.register_introspection_functions()

        # Register RPC functions.
        self.rpc_server.register_function(self.forwardMessage)
        self.rpc_server.register_function(self.registerNewPeer)
        self.rpc_server.register_function(self.leavePeer)

        server_thread = threading.Thread(name="server", target=self._server)
        server_thread.setDaemon(True)  # Don't wait for server thread to exit.
        server_thread.start()

        queue_thread = threading.Thread(name="queue",
                                        target=self._queue_handler)
        queue_thread.setDaemon(True)  # Don't wait for server thread to exit.
        queue_thread.start()

    def shutdown(self):
        self.rpc_server.server_close()

    def registerNewPeer(self, name, host, port):
        peer = BasicPeerHandler(name, host, port)
        peer.setLocation(self.peer_controller.generateNewPeerLocation())
        peer.setPeerController(self.peer_controller)
        self.peer_controller.addPeer(peer)
        print("Peer " + name + " joined")
        return ''

    def leavePeer(self, name):
        for peer in self.peers:
            if peer.name == name:
                self.peer_controller.removePeer(peer)
                print("Peer " + name + " left")
                break
        return ''

    # Peer name is the origin of the message
    def forwardMessage(self, immediate_sender, msg_id, peer_name, msgtype,
                       argdict):
        with self.statlock:
            self.msgshandled += 1
            self.msgsize += sys.getsizeof(argdict)
            if not msgtype in self.msgtypecnt:
                self.msgtypecnt[msgtype] = 0
            self.msgtypecnt[msgtype] += 1
        #Find peer
        #TODO: IMPROVE
        immediate_sender_peer = None
        for peer in self.peers:
            if peer.name == immediate_sender:
                immediate_sender_peer = peer
                break
        self.msg_lock.acquire()
        self.msg_queue.append(
            (immediate_sender_peer, msg_id, peer_name, msgtype, argdict))
        self.queue_was_empty = False
        self.msg_cond.notify()
        self.msg_lock.release()
        return ''

    def _server(self):
        print('Starting router on: %s:%s' % (self.host, self.port))
        try:
            self.rpc_server.serve_forever()
        except ValueError:  # Thrown upon exit :(
            pass

    def _queue_handler(self):
        while True:
            with self.msg_lock:
                if not self.queue_active:
                    self.msg_cond.wait()
                    continue
                if len(self.msg_queue) == 0:
                    self.msg_cond.wait()
                for i in range(len(self.msg_queue)):
                    (immediate_sender_peer, msg_id, peer_name, msgtype,
                     argdict) = self.msg_queue[i]
                    if not self.useTicks or (
                            self.peer_can_send[immediate_sender_peer.name]
                            and self.peer_can_send[immediate_sender_peer.name]
                            > 0):
                        self.msg_queue.pop(i)
                        if self.useTicks:
                            self.peer_can_send[immediate_sender_peer.name] -= 1
                        self._do_forward_msg(immediate_sender_peer, msg_id,
                                             peer_name, msgtype, argdict)
                        break

    def _do_forward_msg(self, immediate_sender_peer, msg_id, peer_name,
                        msgtype, argdict):
        peersInRange = self.peer_controller.findPeersInRange(
            immediate_sender_peer)
        for peer in peersInRange:
            if peer != immediate_sender_peer:
                peer.sendMessage(immediate_sender_peer, msg_id, peer_name,
                                 str(msgtype), argdict)

    def _main_loop(self):
        while True:
            cmd = sys.stdin.readline().strip()
            if "q" == cmd:
                break
            if "setLocation " in cmd:
                txt = cmd.replace("setLocation ", "").strip()
                loc = txt.split(" ")
                for p in self.peers:
                    if p.name == loc[0]:
                        p.setLocation((int(loc[1]), int(loc[2]), int(loc[3]),
                                       int(loc[4])))
                        break
                continue
            if "movePeers" == cmd:
                self.peer_controller.movePeers()
                continue
            print("Unknown command:", cmd)