Example #1
0
class Advertiser:
    """ The Advertiser is used for SERVICE DISCOVERY to advertise the address of a checkers server """
    def __init__(self, server_ip: str, broadcast_ip: str, broadcast_port: int,
                 listen_port: int, logger: Logger):
        """
        :param server_ip: IP to bind the UDP server to
        :param broadcast_ip: IP to broadcast messages to
        :param broadcast_port: Port to broadcast messages to
        """
        self.__ip = server_ip
        self.__port = broadcast_port
        self.__broadcast_ip = broadcast_ip
        self.__listen_port = listen_port
        self.__advertiser: UDP = None
        self.__advertising_timer: Timer = None
        self.__logger = logger

    def start(self, loop: Loop):
        """
        Add the UDP socket and a Timer to the event loop
        :param loop: The Event Loop to bind the objects to
        """
        self.__logger.info("Registering Advertiser on {ip}:{port}".format(
            ip=self.__ip, port=self.__listen_port))
        self.__advertiser = UDP(loop)
        # Bind to the same IP and Port as the TCP server
        self.__advertiser.bind((self.__ip, self.__listen_port))
        # Enable Broadcast
        self.__advertiser.set_broadcast(True)

        # Create a timer to send a broadcast consistently
        self.__advertising_timer = Timer(loop)
        self.__advertising_timer.start(self.advertise, 1, 1)

    def advertise(self, timer_handle: Timer):
        """
        Send a UDP Broadcast to the port to show that this server is accepting connections to play checkers
        :param timer_handle: The timer handle, unused
        """
        self.__logger.debug("Broadcasting existence to {ip}:{port}".format(
            ip=self.__broadcast_ip, port=self.__port))
        self.__advertiser.try_send((self.__broadcast_ip, self.__port), b"")

    def stop(self):
        """
        Stop the timer and close the UDP server when stopping the advertiser
        """
        self.__logger.info("Shutting down Advertiser")
        if self.__advertising_timer:
            self.__advertising_timer.stop()
        if self.__advertiser:
            self.__advertiser.close()
Example #2
0
import time
from pyuv import Loop
from pyuv import Timer

counter = 0
def timer_callback(handle):
	global counter
	counter += 1
	if counter < 10:
		print 'counter = %d, %s' % (counter, time.strftime('%H:%M:%S'))
	else:
		handle.stop()

loop = Loop.default_loop()

handle = Timer(loop)
handle.start(timer_callback, 3.5, 2) # timeout = 3.5s, repeat = 2s

print 'timer start,', time.strftime('%H:%M:%S')
loop.run()

class Client:
    """
    STATEFUL
    Represents a User playing checkers
    The .__protocol_state property shows what state of the protocol this client is currently in
    """
    def __init__(self, ip: str, port: int, username: bytes, password: bytes,
                 interface: Interface, logger: Logger) -> None:
        """
        Constructor for the Client class
        :param ip: The IP Address for the server to connect to
        :param port: The Port to connect to on the remove server
        :param username: Username to connect with
        :param password: Password to connect with
        :param interface: The User Interface object to allow a user to interface with the game
        :param logger: Logger to write messages to
        """
        # Private Properties
        self.__ip = ip
        self.__port = port
        self.__tcp_handle: TCP = None
        self.__interrupt_handle: Signal = None
        self.__username = username
        self.__password = password
        self.__protocol_state = ProtocolState.UNAUTHENTICATED
        self.__ui = interface
        self.__logger = logger
        # Timeout settings
        self.__in_timeout = False
        self.__timeout_buffer = b""
        self.__timeout_timer: Timer = None
        self.__timeout_duration = 1

    # Public Methods

    def start(self, loop: Loop) -> None:
        """
        Attach the client to the event loop
        CLIENT

        :param loop: Event Loop object to attach to
        """
        # Register the TCP client to connect to the server
        self.__tcp_handle = TCP(loop)
        self.__tcp_handle.connect((self.__ip, self.__port),
                                  self.__on_connection_start)

        # Register the interrupt handler to catch sigint
        self.__interrupt_handle = Signal(loop)
        self.__interrupt_handle.start(self.__on_signal, SIGINT)

        # Create the timeout handler
        self.__timeout_timer = Timer(loop)

    def shutdown(self) -> None:
        """
        Shut the client down by telling the server it is logging out (if it is logged in)
        and then closing the connection
        """
        self.__logger.info("Shutting Down...")
        if self.__protocol_state != ProtocolState.UNAUTHENTICATED:
            self.__send(LogOut())
        self.__close()

    # Private Methods

    def __send(self, msg: Message) -> None:
        """
        Encode and Send a message to the server
        :param msg: Message to send
        """
        self.__logger.debug("Sending {msg}, encoded as {raw}".format(
            msg=msg, raw=msg.encode()))
        self.__tcp_handle.write(msg.encode())

    def __close(self):
        """
        Close the connection to the server and shut down the signal handler
        """
        self.__tcp_handle.stop_read()
        self.__tcp_handle.shutdown()
        self.__interrupt_handle.close()

    # Event Handles

    def __on_connection_start(self, handler: TCP, error: Optional[int]):
        """
        Called when the TCP connection is started
        :param handler: TCP Socket that is connected to the server
        :param error: An Error would be here if it failed to connect to the server
        """
        if error is not None:
            if error == -4095:
                self.__logger.warning("Got End of File from server")
                self.shutdown()
            else:
                self.__logger.error(
                    "Unknown TCP Error: {stringed}, errno: {no}".format(
                        stringed=errno.strerror(error), no=error))
                self.shutdown()
        else:
            self.__logger.info(
                "Connected to server at {ip}".format(ip=self.__ip))
            # Start Asynchronous Read on the TCP Socket
            handler.start_read(self.__on_data)
            # Send the Log In Message
            self.__send(Connect(1, self.__username, self.__password))
            self.__protocol_state = ProtocolState.IN_QUEUE

    def __on_data(self, client: TCP, data: bytes, error: Optional[int]):
        """
        Called when the TCP socket receives data
        :param client: The TCP Socket that received Data
        :param data: Read Data
        :param error: An Error would be here if it failed to read data
        """
        if error is not None:
            if error == -4095:
                self.__logger.warning("Got End of File from server")
                self.shutdown()
            else:
                self.__logger.error(
                    "Unknown TCP Error: {stringed}, errno: {no}".format(
                        stringed=errno.strerror(error), no=error))
                self.shutdown()
        else:
            # Parse the Message
            if self.__timeout_buffer != b"":
                data = self.__timeout_buffer + data
                self.__timeout_buffer = b""
            try:
                # Clear out the timeout buffer
                if self.__in_timeout:
                    self.__timeout_timer.stop()
                    self.__in_timeout = False
                while data != b"":
                    msg = message_to_type(data).parse_and_decode(data)
                    self.__logger.debug(
                        "Got {message} in state {state}".format(
                            message=msg, state=self.__protocol_state.name))
                    data = data[msg.calc_size() + 1:]
                    # Dispatch to another function based on current DFA state
                    {
                        ProtocolState.UNAUTHENTICATED:
                        self.__msg_on_unauthenticated,
                        ProtocolState.IN_QUEUE: self.__msg_on_in_queue,
                        ProtocolState.PROCESSING_GAME_STATE:
                        self.__msg_on_processing_game_state,
                        ProtocolState.USER_MOVE: self.__msg_on_user_move,
                        ProtocolState.GAME_END: self.__msg_on_game_end
                    }[self.__protocol_state](msg)
            except NotEnoughData:

                def timeout(timer_handle: Timer):
                    self.__logger.critical(
                        "Got invalid incomplete message ({raw}) from server, closing connections"
                        .format(raw=self.__timeout_buffer))
                    self.shutdown()

                # If already in timeout, restart the timer
                if self.__in_timeout:
                    self.__timeout_timer.stop()
                # Append data to the buffer
                self.__timeout_buffer += data
                # Set in timeout
                self.__in_timeout = True
                # Start timer to close connection after timeout duration if needed
                self.__timeout_timer.start(timeout, self.__timeout_duration, 0)
            except InvalidType:
                self.__logger.critical(
                    "Got Invalid Message Type in message ({raw}) from server, closing connections"
                    .format(raw=data))
                self.shutdown()
            except StructError:
                self.__logger.critical(
                    "Got Invalid Message ({raw}) from server, closing connections"
                    .format(raw=data))
                self.shutdown()

    def __on_signal(self, sig_handler: Signal, signal: int) -> None:
        """
        Callback to be called when a signal is caught
        :param sig_handler: The Signal Handler that caught the signal
        :param signal: The signal that was caught
        """
        self.__logger.info("Received Signal {}".format(signal))
        self.shutdown()

    # State based event handlers
    # These functions implement the Client side of the DFA
    # All of the following functions are STATEFUL

    def __msg_on_unauthenticated(self, msg: Message) -> None:
        """
        Logic to do while in the Unauthenticated state
        :param msg: Message received
        """
        self.__logger.fatal(
            "Received invalid message of type {type} for state {state}".format(
                type=msg.__class__.__name__, state=self.__protocol_state.name))
        self.shutdown()

    def __msg_on_in_queue(self, msg: Message) -> None:
        """
        Logic to do while in the In Queue state
        :param msg: Message received
        """
        if isinstance(msg, InvalidLogin):
            # This is the edge of the DFA from In Queue -> Unauthenticated (Invalid Login)
            self.__protocol_state = ProtocolState.UNAUTHENTICATED
            bad_login_reasons = {
                InvalidLogin.Reasons.InvalidPassword: "******",
                InvalidLogin.Reasons.AlreadyLoggedIn:
                "User is already logged in",
                InvalidLogin.Reasons.AccountDoesNotExist: "User does not exist"
            }
            self.__ui.display_message("Invalid Login, {reason}".format(
                reason=bad_login_reasons[msg.reason]))
            # Request a new set of credentials from the user
            self.__username, self.__password = self.__ui.request_credentials()
            self.__send(Connect(1, self.__username, self.__password))
            self.__protocol_state = ProtocolState.IN_QUEUE
        elif isinstance(msg, InvalidVersion):
            # This is the edge of the DFA from In Queue -> Unauthenticated (Invalid Login)
            self.__ui.display_message(
                "The Server does not support your version of the client, it supports versions {lowest}-{highest}"
                .format(lowest=msg.lowest_supported_version,
                        highest=msg.highest_supported_version))
            self.shutdown()
        elif isinstance(msg, QueuePosition):
            # This is the edge of the DFA from In Queue -> In Queue (Queue Pos)
            self.__ui.show_queue_position(msg.queue_pos, msg.rating,
                                          msg.queue_size)
        elif isinstance(msg, GameStart):
            # This is the edge of the DFA from In Queue -> Processing Game State (Game Start)
            self.__ui.game_start(msg.opponent_name.decode("utf-8"),
                                 msg.opponent_rating)
            self.__protocol_state = ProtocolState.PROCESSING_GAME_STATE
        else:
            self.__logger.fatal(
                "Received invalid message of type {type} for state {state}".
                format(type=msg.__class__.__name__,
                       state=self.__protocol_state.name))
            self.shutdown()

    def __msg_on_processing_game_state(self, msg: Message):
        """
        Logic to do while in the Processing Game State state
        :param msg: Message received
        """
        if isinstance(msg, YourTurn):
            # This is the edge of the DFA from Processing Game State -> User Move (Your Turn)
            self.__protocol_state = ProtocolState.USER_MOVE
            # Check if this is the beginning of the game
            if msg.last_move == Move(0, 0, Direction.Negative,
                                     Direction.Negative):
                self.__ui.display_message("You go first!")
            else:
                self.__ui.display_message("Last Move was : {move}".format(
                    move=msg.last_move.__repr__()))
            self.__ui.display(msg.board)
            self.__send(MakeMove(self.__ui.get_move()))
            self.__protocol_state = ProtocolState.PROCESSING_GAME_STATE
        elif isinstance(msg, CompulsoryMove):
            # This is the edge of the DFA from Processing Game State -> Processing Game State (Compulsory Move)
            self.__ui.display_message(
                "Compulsory Move: {move}".format(move=msg.move.__repr__()))
            self.__ui.display(msg.board)
        elif isinstance(msg, InvalidMove):
            # This is the edge of the DFA from Processing Game State -> User Move (Invalid Move)
            self.__protocol_state = ProtocolState.USER_MOVE
            self.__ui.display_message(
                "{move} Is an invalid move".format(move=msg.move.__repr__()))
            self.__ui.display(msg.board)
            self.__send(MakeMove(self.__ui.get_move()))
            self.__protocol_state = ProtocolState.PROCESSING_GAME_STATE
        elif isinstance(msg, GameOver):
            # This is the edge of the DFA from Processing Game State -> Game End (Game Over)
            self.__protocol_state = ProtocolState.GAME_END
            self.__ui.game_over(msg.board, msg.old_rating, msg.new_rating,
                                msg.you_won != 0)
            if self.__ui.prompt_play_again():
                self.__protocol_state = ProtocolState.IN_QUEUE
                self.__send(ReQueue())
            else:
                self.shutdown()

        elif isinstance(msg, OpponentDisconnect):
            # This is the edge of the DFA from Processing Game State -> Game End (Opponent Disconnect)
            self.__protocol_state = ProtocolState.GAME_END
            self.__ui.opponent_left()
            if self.__ui.prompt_play_again():
                self.__protocol_state = ProtocolState.IN_QUEUE
                self.__send(ReQueue())
            else:
                self.shutdown()
        else:
            self.__logger.fatal(
                "Received invalid message of type {type} for state {state}".
                format(type=msg.__class__.__name__,
                       state=self.__protocol_state.name))
            self.shutdown()

    def __msg_on_user_move(self, msg: Message):
        """
        Logic to do while in the User Move state
        :param msg: Message received
        """
        if isinstance(msg, OpponentDisconnect):
            # This is the edge of the DFA from User Move -> Game End (Opponent Disconnect)
            self.__protocol_state = ProtocolState.GAME_END
            self.__ui.opponent_left()
            if self.__ui.prompt_play_again():
                self.__protocol_state = ProtocolState.IN_QUEUE
                self.__send(ReQueue())
            else:
                self.shutdown()
        else:
            self.__logger.fatal(
                "Received invalid message of type {type} for state {state}".
                format(type=msg.__class__.__name__,
                       state=self.__protocol_state.name))
            self.shutdown()

    def __msg_on_game_end(self, msg: Message):
        """
        Logic to do while in the Game End state
        :param msg: Message Received
        """
        self.__logger.fatal(
            "Received invalid message of type {type} for state {state}".format(
                type=msg.__class__.__name__, state=self.__protocol_state.name))
        self.shutdown()
Example #4
0
import time
from pyuv import Loop
from pyuv import Timer

counter = 0


def timer_callback(handle):
    global counter
    counter += 1
    if counter < 10:
        print 'counter = %d, %s' % (counter, time.strftime('%H:%M:%S'))
    else:
        handle.stop()


loop = Loop.default_loop()

handle = Timer(loop)
handle.start(timer_callback, 3.5, 2)  # timeout = 3.5s, repeat = 2s

print 'timer start,', time.strftime('%H:%M:%S')
loop.run()