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()