Exemplo n.º 1
0
class Peer_Remote():  # outbound connections
    def __init__(self, network_service, remote_ip, remote_port, context=None):
        self.exit = False
        self.network_service = network_service
        self.remote_ip = remote_ip
        self.remote_port = remote_port
        self.context = context

        Coro(self._server_connect)

    def _server_connect(self, coro=None):
        try:
            #logger.debug('CLIENT: connecting to peer at %s:%s', self.remote_ip, str(self.remote_port))
            self.outbound_socket = AsynCoroSocket(socket.socket(socket.AF_INET, socket.SOCK_STREAM))
            self.outbound_socket.setsockopt(socket.SOL_TCP, socket.TCP_NODELAY, 1) # if you're gonna act like UDP

            yield self.outbound_socket.connect((self.remote_ip, self.remote_port))
            #logger.debug('CLIENT: connected to peer at %s:%s', self.remote_ip, str(self.remote_port))
            self._send_coro = Coro(self._client_send)
            #Coro(self._client_recv) # unneeded if we don't utilize bi-directional communication in UDP style messaging

            self.network_service.on_server_connect(self, self.context)
        except:
            show_error()
            #raise

    def _client_recv(self, coro=None):
        while True:
            try:
                data = yield self.outbound_socket.recv_msg()
                if data == None or len(data) == 0 or self.exit:
                    break
                #logger.debug('CLIENT: received data to peer at %s:%s (Data: %s)', self.remote_ip, str(self.remote_port), data)
                self.network_service.on_peer_data_received(data)
            except:
                show_error()
                #break
        #print "Coro(_client_recv) exiting"

    def _client_send(self, coro=None):
        coro.set_daemon()
        while True:
            try:
                cmd, state = yield self._send_coro.receive()
                data, context = state
                if cmd == NETWORK_PEER_DISCONNECT:
                    self.network_service.on_client_disconnected(context)
                    break

                #logger.debug('CLIENT: sending data to %s:%s (Data is: %s)', self.remote_ip, self.remote_port,data)
                yield self.outbound_socket.send_msg(data)
                self.network_service.on_client_data_sent(context)
            except:
                show_error()
                #break

        self.outbound_socket.shutdown(socket.SHUT_RDWR)
        self.outbound_socket.close()
        #logger.debug('CLIENT: disconnected from %s:%s', self.remote_ip, str(self.remote_port))
        #print "Coro(_client_send) exiting"


    def send(self, data, context):
        if not self.exit:
            self._send_coro.send((None, (data, context)))

    def stop(self, context=None):
        self.exit = True
        #logger.debug('CLIENT: disconnecting from %s:%s', self.remote_ip, str(self.remote_port))
        self._send_coro.send((NETWORK_PEER_DISCONNECT, (None,context)))
Exemplo n.º 2
0
class Node_Service(Service):
    exit = False
    pause_scheduler = False

    def __init__(self, message_router):
        super(Node_Service, self).__init__(SERVICE_NODE, message_router, cl_name="ns")
        self.exit = False
        self.pause_scheduler = False
        self.nodes = {}
        self.queue = deque()
        self.delay_queue = deque()

        self.scheduler_thread = Thread(target=self._thread_scheduler)
        self.scheduler_thread.daemon = True
        self.scheduler_thread.start()

        self._stabilize_coro = Coro(self._coro_stabilize)

    def delay_enqueue(self,message,ms):
        if not self.pause_scheduler:
            self.delay_queue.append( (time.time(), ms, message))

    def _thread_scheduler(self):
        try:
            while not self.exit:
                requeue = deque()

                while len(self.delay_queue) > 0 and not self.exit:
                    queued_at, delay_ms, message = self.delay_queue.popleft()
                    if (queued_at + delay_ms / 1000) < time.time():
                        #self.enqueue(message)
                        self._stabilize_coro.send(message)
                    else:
                        requeue.append((queued_at, delay_ms, message))

                while len(requeue) > 0 and not self.exit:
                    self.delay_queue.append(requeue.popleft())
                del requeue
                time.sleep(.1)  # 10 ms
        except:
            show_error()
        print "Scheduler thread exiting"

    def _coro_stabilize(self, coro=None):
        coro.set_daemon()

        thread_pool = AsynCoroThreadPool(2 * multiprocessing.cpu_count())
        while not self.exit:
            try:
                command, context = yield self._stabilize_coro.receive()

                if self.exit:  # fast exit for now (non-graceful)
                    break

                #command, context = self.queue.popleft()
                if self.pause_scheduler or context.join_on_stabilize:  # just cycle the messages until unpaused
                    if context.join_on_stabilize:
                        context.send_message(Find_Successor_Message(context.thisNode, context.thisNode.key, context.thisNode), context.join_on_stabilize)
                        context.join_on_stabilize = None
                        delay = MAINTENANCE_PERIOD * 3
                    else:
                        delay = MAINTENANCE_PERIOD

                    self.delay_enqueue((command,context), delay)
                    continue


                if command == "NODE_STABILIZE":
                    yield thread_pool.async_task(coro, context.begin_stabilize)
                    self.delay_enqueue( ("NODE_CHECK_PREDECESSOR", context), MAINTENANCE_PERIOD)
                elif command == "NODE_CHECK_PREDECESSOR":
                    yield thread_pool.async_task(coro, context.begin_stabilize)
                    yield thread_pool.async_task(coro, context.check_predecessor)
                    self.delay_enqueue( ("NODE_FIX_FINGERS", context), MAINTENANCE_PERIOD )
                elif command == "NODE_FIX_FINGERS":
                    yield thread_pool.async_task(coro, context.fix_fingers, 10)
                    self.delay_enqueue( ("NODE_STABILIZE", context), MAINTENANCE_PERIOD)
            except:
                show_error()

        print "Coro(stabilize) exiting"


    def stop(self, context=None):
        self.exit = True
        self._stabilize_coro.send((None,None))
        #for i in range(2 * multiprocessing.cpu_count()):
            #self.queue.append(('terminate', context))
            #self.signal_item_queued.set()

    def get_console_node(self):
        if len(self.nodes) > 0:
            return self.nodes[self.nodes.keys()[0]]

    def check_scheduler_pause(self,msg):
        if not self.pause_scheduler:
            return False

        if msg.type == Message_Recv_Peer_Data.Type():
            msg = msg.network_msg

        result = False
        if msg.type == Stabilize_Reply_Message.Type():
            result = True
        elif msg.type == Stablize_Message.Type():
            result = True
        elif msg.type == Check_Predecessor_Message.Type():
            result = True
        elif msg.type == Update_Message.Type():
            result = True
        elif msg.type == Find_Successor_Message.Type():
            result = True
        return result

    def handle_message(self, msg):
        if not msg.dest_service == self.service_id:
            raise Exception("Mismatched service recipient for message.")

        if msg.type == Message_Setup_Node.Type():
            node = Node(self, msg.public_ip, msg.local_ip, msg.local_port)
            if msg.seeded_peers:
                for peer in msg.seeded_peers:
                    node.join(peer) # use callback to try next peer if join fails (join is async)
            else:
                node.join()

            self.message_router.route(
                Message_Start_Server(node.local_ip, node.local_port,
                        Message_Start_Server_Callback(self.service_id, node, True),
                        Message_Start_Server_Callback(self.service_id, node, False)))

        elif msg.type == Message_Start_Server_Callback.Type():
            if msg.result:
                self.nodes[str(msg.node)] = msg.node
                self.delay_enqueue(("NODE_STABILIZE", msg.node), 1000)
            else:
                msg.node.exit_network()
                raise Exception( "Unable to successfully start server for node at " + str(msg.node.thisNode))
        if msg.type == Message_Forward.Type( ):
            ni = msg.origin_node
            if self.nodes.has_key(str(ni)):
                lnode = self.nodes[str(ni)] # get the Node class this message is addressed to (ip:port)
                forward_node = lnode.find_ideal_forward(msg.forward_hash)

                if msg.forward_msg.type == Database_Get_Message.Type() or msg.forward_msg.type == Database_Put_Message.Type():
                    msg.forward_msg.storage_node = forward_node

                if forward_node != ni:
                    self.send_message(msg.forward_msg, forward_node)
                else:
                    self.send_message(msg.forward_msg)
        elif msg.type == Message_Recv_Peer_Data.Type(): # came off the wire
            ni = Node_Info(msg.local_ip if len(msg.local_ip) > 0 else "127.0.0.1",msg.local_port)
            if self.nodes.has_key(str(ni)):
                lnode = self.nodes[str(ni)] # get the Node class this message is addressed to (ip:port)

                msg = msg.network_msg
                rnode = lnode.final_destination(msg)
                if rnode != lnode.thisNode:
                    self.send_message(msg,rnode)
                    return

                logger.debug(str(ni) + " received network msg: " + msg.type)

                if msg.type == Message_Forward.Type( ):
                    lnode.find_ideal_forward(msg.forward_hash) #fix this...we need to do forward the message
                elif msg.type == Find_Successor_Message.Type():
                    self.send_message(Update_Message(lnode.thisNode, msg.reply_to.key, msg.finger), msg.reply_to)
                elif msg.type == Update_Message.Type( ):
                    lnode.update_finger(msg.reply_to, msg.finger)
                elif msg.type == Check_Predecessor_Message.Type():
                    self.send_message(Update_Message(lnode.thisNode, msg.reply_to.key, 0), msg.reply_to)
                elif msg.type == Stablize_Message.Type( ):
                    self.send_message(Stabilize_Reply_Message(lnode.thisNode, msg.reply_to.key, msg.reply_to))
                elif msg.type == Stabilize_Reply_Message.Type():
                    lnode.stabilize(msg)
                elif msg.type == Notify_Message.Type():
                    lnode.get_notified(msg)
                elif msg.type == Exit_Message.Type():
                    lnode.peer_polite_exit(msg.reply_to)
                elif msg.type == Database_Put_Message.Type() or msg.type == Database_Get_Message.Type():
                    msg.storage_node = lnode.thisNode
                    self.message_router.route(msg)
                elif msg.type == Database_Put_Message_Response.Type() or msg.type == Database_Get_Message_Response.Type():
                    self.message_router.route(msg)


        elif msg.type == Message_Console_Command.Type():
            self.handle_command(msg.command,msg.args)

    # wraps message in network packet if not destined for local
    #def route_node_message(self,msg):
    #    if self.nodes.has_key(str(msg.origin_node)):
    #        node = self.nodes[str(msg.origin_node)]
    #        forward_to = node.find_ideal_forward(msg.destination_key)
    #        if forward_to == node:
    #            self.send_message(msg)
    #        else:
    #            self.send_message(Message_Send_Peer_Data(forward_to, msg.serialize()))

    def handle_command(self,cmd,args=[]):
        if cmd == "print":
            for node in self.nodes.values():
                print "successor  ", node.thisNode.successor.print_key()
                print "predecessor", node.thisNode.predecessor.print_key()
        elif cmd == "pause":
            self.pause_scheduler = True
        elif cmd == "resume":
            self.pause_scheduler = False