pantools.TCPServer API documentation

# Module `pantools.TCPServer`

Expand source code

    import threading
    import time,logging, sys, os
    import socket
    import pickle
    from _thread import *
    from pantools.net import announce_service, wait_for_announcement

    from .logger import logger
    from .send_recv import send_json, send_size, recv_size
    from .ClientConnection import ClientConnection

    # ================================================================
    #
    # ================================================================
    class TCPServer:

        def __init__(self) -> None:
            self.clients = []
            self.subscribers = [] # deprecated
            self.msg_subscribers = {}
            self.ThreadCount = 0
            self.lock = threading.Lock()

        def print_clients(self, header) -> None:
            """Prints a table of active connections, followed by a table of active subscriptions"""

            logger.info("--- connection list begin {} ---".format(header))
            for c in self.clients:
                logger.info(c.getPeerName())
            logger.info("--- connection list end         ---")

            logger.info("--- subscriber list begin ---")
            for msgtype in self.msg_subscribers:
                logger.info("msgtype:{}".format(msgtype))
                for c in self.msg_subscribers[msgtype]:
                    logger.info(c.getPeerName())
            logger.info("--- subscriber list end ---")

        def setup_server(self, host, port, adv_magic=None, adv_port=None) -> None:
            """Creates a socket and starts listening. Also optionally starts an Anouncement thread"""
            self.ServerSocket = socket.socket()
            self.ServerSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            self.ThreadCount = 0

            try:
                logger.info("Binding to {}:{}".format(host, port))
                self.ServerSocket.bind((host, port))
            except socket.error as e:
                logger.info(str(e))
                sys.exit(1)

            logger.info("Listening for a Connection..")
            self.ServerSocket.listen(5)

            if adv_magic is not None and adv_port is not None:
                start_new_thread(self.advertise_service,(adv_magic, adv_port, host, port))

        def advertise_service(self, adv_magic, adv_port, service_host, service_port) -> None:
            logger.info("Starting an advertising thread {} on port {}. Service on {} {}".format(adv_magic, adv_port, service_host, service_port))
            while True:
                announce_service(adv_magic, adv_port, service_host, service_port)
                time.sleep(5)

        def handle_message(self, client, obj) -> None:

            # message : ["image", "subscribe", "unsubscribe", "announce"]
            # msgtype : ["image", "admin", ""]
            message = obj["message"]
            msgtype = obj["msgtype"]

            # filter out the ones we dont want printed out
            if message not in ["image"]:
                logger.debug(f"Received message {message}")

            if message == "subscribe":
                #self.lookup_client(connection)
                self.add_subscriber(msgtype, client)
                self.print_clients("After SUBSCRIBE")

            if message == "unsubscribe":
                self.remove_subscriber(msgtype, client)
                self.print_clients("After UNSUBSCRIBE")

            if message == "image":
                logger.debug("Received image {}".format(obj["frameno"]))
                self.send_message_to_all_subscribers(obj)

            if message == "announce":
                logger.debug("Received announce {}".format(obj["hostname"]))
                client.set_hostname(obj["hostname"])

            # Query Reply!
            if message == "gethosts":
                logger.debug("Received gethosts")

        def read_exception(self, client, e):
            self.remove_client(client)

        def add_client(self, client: ClientConnection) -> None:
            logger.info("Adding connection {}".format(client))
            self.lock.acquire()
            self.clients.append(client)
            self.lock.release()

            self.print_clients("After ADD")

        def remove_client(self, client: ClientConnection) -> None:
            logger.info("Removing connection {}".format(client))

            self.lock.acquire()
            try:
                self.clients.remove(client)
                for msgtype in self.msg_subscribers:
                    if client in self.msg_subscribers[msgtype]:
                        print("Removing subscriber {}".format(client))
                        self.msg_subscribers[msgtype].remove(client)
            except Exception as e:
                logger.error(f"Exception: {e}")
            finally:
                self.lock.release()

            self.print_clients("After REMOVE")

        def add_subscriber(self, msgtype, client) -> None:
            logger.info("Adding subscriber {} {}".format(msgtype, client))
            self.lock.acquire()
            if msgtype not in self.msg_subscribers:
                self.msg_subscribers[msgtype] = []
            self.msg_subscribers[msgtype].append(client)
            self.lock.release()
            self.print_clients("After ADD SUB")

        def remove_subscriber(self, msgtype, client) -> None:
            logger.info("Removing subscriber {} {}".format(msgtype, client))
            self.lock.acquire()
            if msgtype not in self.msg_subscribers:
                self.lock.release()
                return
            try:
                self.msg_subscribers[msgtype].remove(client)
            except Exception as e:
                logger.error(f"Exception : {e}")
            finally:
                self.lock.release()
            self.print_clients("After REMOVE SUB")

        def send_message_to_all_clients(self, msg) -> None:
            pass

        def send_message_to_all_subscribers(self, msg) -> None:
            self.lock.acquire()
            logger.debug("finding subscribers of msgtype {}".format(msg["msgtype"]))
            if msg["msgtype"] in self.msg_subscribers:
                for c in self.msg_subscribers[msg["msgtype"]]:
                    logger.debug("Sending message to {}".format(c))
                    try:
                        send_json(c.getSocket(), msg)
                    except Exception as e:
                        logger.error(f"Exception: {e} when sending message to all clients")

            self.lock.release()

        def accept_clients(self):
            while True:
                logger.debug("accept_clients() thread waiting...")
                client_socket, address = self.ServerSocket.accept()
                logger.info("Client connected: " + address[0] + ":" + str(address[1]))

                client = ClientConnection(client_socket, self)
                self.add_client(client)
                self.print_clients("After CLIENT ACCEPTED")
                client.start_reader()

        def stop_server(self):
            self.ServerSocket.close()

## Classes

`class TCPServer`
  Expand source code

      class TCPServer:

          def __init__(self) -> None:
              self.clients = []
              self.subscribers = [] # deprecated
              self.msg_subscribers = {}
              self.ThreadCount = 0
              self.lock = threading.Lock()

          def print_clients(self, header) -> None:
              """Prints a table of active connections, followed by a table of active subscriptions"""

              logger.info("--- connection list begin {} ---".format(header))
              for c in self.clients:
                  logger.info(c.getPeerName())
              logger.info("--- connection list end         ---")

              logger.info("--- subscriber list begin ---")
              for msgtype in self.msg_subscribers:
                  logger.info("msgtype:{}".format(msgtype))
                  for c in self.msg_subscribers[msgtype]:
                      logger.info(c.getPeerName())
              logger.info("--- subscriber list end ---")

          def setup_server(self, host, port, adv_magic=None, adv_port=None) -> None:
              """Creates a socket and starts listening. Also optionally starts an Anouncement thread"""
              self.ServerSocket = socket.socket()
              self.ServerSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
              self.ThreadCount = 0

              try:
                  logger.info("Binding to {}:{}".format(host, port))
                  self.ServerSocket.bind((host, port))
              except socket.error as e:
                  logger.info(str(e))
                  sys.exit(1)

              logger.info("Listening for a Connection..")
              self.ServerSocket.listen(5)

              if adv_magic is not None and adv_port is not None:
                  start_new_thread(self.advertise_service,(adv_magic, adv_port, host, port))

          def advertise_service(self, adv_magic, adv_port, service_host, service_port) -> None:
              logger.info("Starting an advertising thread {} on port {}. Service on {} {}".format(adv_magic, adv_port, service_host, service_port))
              while True:
                  announce_service(adv_magic, adv_port, service_host, service_port)
                  time.sleep(5)

          def handle_message(self, client, obj) -> None:

              # message : ["image", "subscribe", "unsubscribe", "announce"]
              # msgtype : ["image", "admin", ""]
              message = obj["message"]
              msgtype = obj["msgtype"]

              # filter out the ones we dont want printed out
              if message not in ["image"]:
                  logger.debug(f"Received message {message}")

              if message == "subscribe":
                  #self.lookup_client(connection)
                  self.add_subscriber(msgtype, client)
                  self.print_clients("After SUBSCRIBE")

              if message == "unsubscribe":
                  self.remove_subscriber(msgtype, client)
                  self.print_clients("After UNSUBSCRIBE")

              if message == "image":
                  logger.debug("Received image {}".format(obj["frameno"]))
                  self.send_message_to_all_subscribers(obj)

              if message == "announce":
                  logger.debug("Received announce {}".format(obj["hostname"]))
                  client.set_hostname(obj["hostname"])

              # Query Reply!
              if message == "gethosts":
                  logger.debug("Received gethosts")

          def read_exception(self, client, e):
              self.remove_client(client)

          def add_client(self, client: ClientConnection) -> None:
              logger.info("Adding connection {}".format(client))
              self.lock.acquire()
              self.clients.append(client)
              self.lock.release()

              self.print_clients("After ADD")

          def remove_client(self, client: ClientConnection) -> None:
              logger.info("Removing connection {}".format(client))

              self.lock.acquire()
              try:
                  self.clients.remove(client)
                  for msgtype in self.msg_subscribers:
                      if client in self.msg_subscribers[msgtype]:
                          print("Removing subscriber {}".format(client))
                          self.msg_subscribers[msgtype].remove(client)
              except Exception as e:
                  logger.error(f"Exception: {e}")
              finally:
                  self.lock.release()

              self.print_clients("After REMOVE")

          def add_subscriber(self, msgtype, client) -> None:
              logger.info("Adding subscriber {} {}".format(msgtype, client))
              self.lock.acquire()
              if msgtype not in self.msg_subscribers:
                  self.msg_subscribers[msgtype] = []
              self.msg_subscribers[msgtype].append(client)
              self.lock.release()
              self.print_clients("After ADD SUB")

          def remove_subscriber(self, msgtype, client) -> None:
              logger.info("Removing subscriber {} {}".format(msgtype, client))
              self.lock.acquire()
              if msgtype not in self.msg_subscribers:
                  self.lock.release()
                  return
              try:
                  self.msg_subscribers[msgtype].remove(client)
              except Exception as e:
                  logger.error(f"Exception : {e}")
              finally:
                  self.lock.release()
              self.print_clients("After REMOVE SUB")

          def send_message_to_all_clients(self, msg) -> None:
              pass

          def send_message_to_all_subscribers(self, msg) -> None:
              self.lock.acquire()
              logger.debug("finding subscribers of msgtype {}".format(msg["msgtype"]))
              if msg["msgtype"] in self.msg_subscribers:
                  for c in self.msg_subscribers[msg["msgtype"]]:
                      logger.debug("Sending message to {}".format(c))
                      try:
                          send_json(c.getSocket(), msg)
                      except Exception as e:
                          logger.error(f"Exception: {e} when sending message to all clients")

              self.lock.release()

          def accept_clients(self):
              while True:
                  logger.debug("accept_clients() thread waiting...")
                  client_socket, address = self.ServerSocket.accept()
                  logger.info("Client connected: " + address[0] + ":" + str(address[1]))

                  client = ClientConnection(client_socket, self)
                  self.add_client(client)
                  self.print_clients("After CLIENT ACCEPTED")
                  client.start_reader()

          def stop_server(self):
              self.ServerSocket.close()

  ### Methods

  `def accept_clients(self)`
    Expand source code

        def accept_clients(self):
            while True:
                logger.debug("accept_clients() thread waiting...")
                client_socket, address = self.ServerSocket.accept()
                logger.info("Client connected: " + address[0] + ":" + str(address[1]))

                client = ClientConnection(client_socket, self)
                self.add_client(client)
                self.print_clients("After CLIENT ACCEPTED")
                client.start_reader()

  `def add_client(self, client: ClientConnection) ‑> NoneType`
    Expand source code

        def add_client(self, client: ClientConnection) -> None:
            logger.info("Adding connection {}".format(client))
            self.lock.acquire()
            self.clients.append(client)
            self.lock.release()

            self.print_clients("After ADD")

  `def add_subscriber(self, msgtype, client) ‑> NoneType`
    Expand source code

        def add_subscriber(self, msgtype, client) -> None:
            logger.info("Adding subscriber {} {}".format(msgtype, client))
            self.lock.acquire()
            if msgtype not in self.msg_subscribers:
                self.msg_subscribers[msgtype] = []
            self.msg_subscribers[msgtype].append(client)
            self.lock.release()
            self.print_clients("After ADD SUB")

  `def advertise_service(self, adv_magic, adv_port, service_host, service_port) ‑> NoneType`
    Expand source code

        def advertise_service(self, adv_magic, adv_port, service_host, service_port) -> None:
            logger.info("Starting an advertising thread {} on port {}. Service on {} {}".format(adv_magic, adv_port, service_host, service_port))
            while True:
                announce_service(adv_magic, adv_port, service_host, service_port)
                time.sleep(5)

  `def handle_message(self, client, obj) ‑> NoneType`
    Expand source code

        def handle_message(self, client, obj) -> None:

            # message : ["image", "subscribe", "unsubscribe", "announce"]
            # msgtype : ["image", "admin", ""]
            message = obj["message"]
            msgtype = obj["msgtype"]

            # filter out the ones we dont want printed out
            if message not in ["image"]:
                logger.debug(f"Received message {message}")

            if message == "subscribe":
                #self.lookup_client(connection)
                self.add_subscriber(msgtype, client)
                self.print_clients("After SUBSCRIBE")

            if message == "unsubscribe":
                self.remove_subscriber(msgtype, client)
                self.print_clients("After UNSUBSCRIBE")

            if message == "image":
                logger.debug("Received image {}".format(obj["frameno"]))
                self.send_message_to_all_subscribers(obj)

            if message == "announce":
                logger.debug("Received announce {}".format(obj["hostname"]))
                client.set_hostname(obj["hostname"])

            # Query Reply!
            if message == "gethosts":
                logger.debug("Received gethosts")

  `def print_clients(self, header) ‑> NoneType`

    Prints a table of active connections, followed by a table of active subscriptions

    Expand source code

        def print_clients(self, header) -> None:
            """Prints a table of active connections, followed by a table of active subscriptions"""

            logger.info("--- connection list begin {} ---".format(header))
            for c in self.clients:
                logger.info(c.getPeerName())
            logger.info("--- connection list end         ---")

            logger.info("--- subscriber list begin ---")
            for msgtype in self.msg_subscribers:
                logger.info("msgtype:{}".format(msgtype))
                for c in self.msg_subscribers[msgtype]:
                    logger.info(c.getPeerName())
            logger.info("--- subscriber list end ---")

  `def read_exception(self, client, e)`
    Expand source code

        def read_exception(self, client, e):
            self.remove_client(client)

  `def remove_client(self, client: ClientConnection) ‑> NoneType`
    Expand source code

        def remove_client(self, client: ClientConnection) -> None:
            logger.info("Removing connection {}".format(client))

            self.lock.acquire()
            try:
                self.clients.remove(client)
                for msgtype in self.msg_subscribers:
                    if client in self.msg_subscribers[msgtype]:
                        print("Removing subscriber {}".format(client))
                        self.msg_subscribers[msgtype].remove(client)
            except Exception as e:
                logger.error(f"Exception: {e}")
            finally:
                self.lock.release()

            self.print_clients("After REMOVE")

  `def remove_subscriber(self, msgtype, client) ‑> NoneType`
    Expand source code

        def remove_subscriber(self, msgtype, client) -> None:
            logger.info("Removing subscriber {} {}".format(msgtype, client))
            self.lock.acquire()
            if msgtype not in self.msg_subscribers:
                self.lock.release()
                return
            try:
                self.msg_subscribers[msgtype].remove(client)
            except Exception as e:
                logger.error(f"Exception : {e}")
            finally:
                self.lock.release()
            self.print_clients("After REMOVE SUB")

  `def send_message_to_all_clients(self, msg) ‑> NoneType`
    Expand source code

        def send_message_to_all_clients(self, msg) -> None:
            pass

  `def send_message_to_all_subscribers(self, msg) ‑> NoneType`
    Expand source code

        def send_message_to_all_subscribers(self, msg) -> None:
            self.lock.acquire()
            logger.debug("finding subscribers of msgtype {}".format(msg["msgtype"]))
            if msg["msgtype"] in self.msg_subscribers:
                for c in self.msg_subscribers[msg["msgtype"]]:
                    logger.debug("Sending message to {}".format(c))
                    try:
                        send_json(c.getSocket(), msg)
                    except Exception as e:
                        logger.error(f"Exception: {e} when sending message to all clients")

            self.lock.release()

  `def setup_server(self, host, port, adv_magic=None, adv_port=None) ‑> NoneType`

    Creates a socket and starts listening. Also optionally starts an Anouncement thread

    Expand source code

        def setup_server(self, host, port, adv_magic=None, adv_port=None) -> None:
            """Creates a socket and starts listening. Also optionally starts an Anouncement thread"""
            self.ServerSocket = socket.socket()
            self.ServerSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            self.ThreadCount = 0

            try:
                logger.info("Binding to {}:{}".format(host, port))
                self.ServerSocket.bind((host, port))
            except socket.error as e:
                logger.info(str(e))
                sys.exit(1)

            logger.info("Listening for a Connection..")
            self.ServerSocket.listen(5)

            if adv_magic is not None and adv_port is not None:
                start_new_thread(self.advertise_service,(adv_magic, adv_port, host, port))

  `def stop_server(self)`
    Expand source code

        def stop_server(self):
            self.ServerSocket.close()