From 73c054162954107666c836db8b284762cd217ca0 Mon Sep 17 00:00:00 2001 From: Bastian Schroll Date: Sun, 7 Jan 2018 21:50:41 +0100 Subject: [PATCH] add server/client class --- boswatch/descriptor/__init__.py | 1 + boswatch/network/client.py | 99 +++++++++++++++++++++++ boswatch/network/server.py | 136 ++++++++++++++++++++++++++++++++ boswatch/utils/paths.py | 39 +++++++++ 4 files changed, 275 insertions(+) create mode 100644 boswatch/descriptor/__init__.py create mode 100644 boswatch/network/client.py create mode 100644 boswatch/network/server.py create mode 100644 boswatch/utils/paths.py diff --git a/boswatch/descriptor/__init__.py b/boswatch/descriptor/__init__.py new file mode 100644 index 0000000..9bad579 --- /dev/null +++ b/boswatch/descriptor/__init__.py @@ -0,0 +1 @@ +# coding=utf-8 diff --git a/boswatch/network/client.py b/boswatch/network/client.py new file mode 100644 index 0000000..6c7f211 --- /dev/null +++ b/boswatch/network/client.py @@ -0,0 +1,99 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +"""! + ____ ____ ______ __ __ __ _____ + / __ )/ __ \/ ___/ | / /___ _/ /______/ /_ |__ / + / __ / / / /\__ \| | /| / / __ `/ __/ ___/ __ \ /_ < + / /_/ / /_/ /___/ /| |/ |/ / /_/ / /_/ /__/ / / / ___/ / +/_____/\____//____/ |__/|__/\__,_/\__/\___/_/ /_/ /____/ + German BOS Information Script + by Bastian Schroll + +@file: client.py +@date: 09.12.2017 +@author: Bastian Schroll +@description: Class implementation for a TCP socket client +""" +import logging +import socket + +logging.debug("- %s loaded", __name__) + + +class TCPClient: + """!TCP client class""" + + def __init__(self, timeout=3): + """!Create a new instance""" + try: + self._sock = None + self._timeout = timeout + except: # pragma: no cover + logging.exception("cannot create a TCPClient") + + def connect(self, host="localhost", port=8080): + """!Connect to the server + + @param host: Server IP address (localhost) + @param port: Server Port (8080) + @return True or False""" + try: + self._sock = socket + self._sock.setdefaulttimeout(self._timeout) + self._sock = socket.create_connection((host, port)) + + logging.debug("connected to " + str(host) + ":" + str(port)) + return True + except ConnectionRefusedError: + logging.error("cannot connect - connection refused") + return False + except: # pragma: no cover + logging.exception("cannot connect to " + str(host) + ":" + str(port)) + return False + + def disconnect(self): + """!Disconnect from the server + + @return True or False""" + try: + self._sock.close() + logging.debug("disconnected") + return True + except AttributeError: + logging.error("cannot disconnect - no connection established") + return False + except: # pragma: no cover + logging.exception("error while disconnecting") + return False + + def transmit(self, data): + """!Send a data packet.py to the server + + @param data: data to send to the server + @return True or False""" + try: + logging.debug("transmitting: " + data) + self._sock.sendall(bytes(data + "\n", "utf-8")) + logging.debug("transmitted...") + return True + except AttributeError: + logging.error("cannot transmit - no connection established") + return False + except: # pragma: no cover + logging.exception("error while transmitting") + return False + + def receive(self): + """!Receive data from the server + + @return received data""" + try: + received = str(self._sock.recv(1024), "utf-8") + logging.debug("received: " + received) + return received + except AttributeError: + logging.error("cannot receive - no connection established") + return False + except: # pragma: no cover + logging.exception("error while receiving") + return False diff --git a/boswatch/network/server.py b/boswatch/network/server.py new file mode 100644 index 0000000..c42ef18 --- /dev/null +++ b/boswatch/network/server.py @@ -0,0 +1,136 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +"""! + ____ ____ ______ __ __ __ _____ + / __ )/ __ \/ ___/ | / /___ _/ /______/ /_ |__ / + / __ / / / /\__ \| | /| / / __ `/ __/ ___/ __ \ /_ < + / /_/ / /_/ /___/ /| |/ |/ / /_/ / /_/ /__/ / / / ___/ / +/_____/\____//____/ |__/|__/\__,_/\__/\___/_/ /_/ /____/ + German BOS Information Script + by Bastian Schroll + +@file: server.py +@date: 11.12.2017 +@author: Bastian Schroll +@description: Class implementation for a threaded TCP socket server +""" +import logging +import socketserver +import threading + +logging.debug("- %s loaded", __name__) + +_clients = [] # module wide global list for received data sets + + +class TCPHandler(socketserver.BaseRequestHandler): + """!RequestHandler class for our TCPServer class.""" + + def handle(self): + """!Handles the request from an single client in a own thread + + Insert a request in the clients[] list and send a [ack]""" + data = 1 + cur_thread = threading.current_thread() + req_name = str(cur_thread) + " " + self.client_address[0] + + try: + while data: + data = str(self.request.recv(1024).strip(), 'utf-8') + if data is not "": + logging.debug(req_name + " recv: " + data) + + # add a new entry at first position (index 0) with client IP + # and the decoded data dict as an string in utf-8 + _clients.insert(0, (self.client_address[0], data)) + logging.debug("Add data to queue") + + logging.debug(req_name + " send: [ack]") + self.request.sendall(bytes("[ack]", "utf-8")) + self.request.close() + + except (ConnectionResetError, ConnectionAbortedError): # pragma: no cover + logging.debug(req_name + " connection closed") + except: # pragma: no cover + logging.exception(req_name + " error while receiving") + + +class TCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer): + """!TCP server class""" + + def __init__(self, timeout=3): + """!Create a new instance""" + self._server = None + self._server_thread = None + self._timeout = timeout + + def start(self, port=8080): + """!Start a threaded TCP socket server + + Start a TCP Socket Server in a new thread that will + then start one more thread for each client request. + The ip address for binding the server socket is always 'localhost' + + @param port: Server Port (8080) + + @return True or False""" + try: + self._server = socketserver.ThreadingTCPServer(("localhost", port), TCPHandler) + self._server.timeout = self._timeout + + self.flushData() + + self._server_thread = threading.Thread(target=self._server.serve_forever) + self._server_thread.daemon = True + self._server_thread.start() + logging.debug("TCPServer started in Thread: " + self._server_thread.name) + return True + except OSError: + logging.exception("server always running?") + except: # pragma: no cover + logging.exception("cannot start the server") + return False + + def stop(self): + """!Stops the TCP socket server + + @return True or False""" + try: + self._server.shutdown() + self._server_thread.join() + self._server.socket.close() + logging.debug("TCPServer stopped") + return True + except AttributeError: + logging.exception("cannot stop - server not started?") + return False + except: # pragma: no cover + logging.exception("cannot stop the server") + return False + + def clientsConnected(self): # pragma: no cover + """!Number of currently connected Clients + + @todo works not safe atm + @return Connected clients""" + if threading.active_count() > 2: + # must subtract the server() and the serve() Thread + return threading.active_count() - 2 + else: + return 0 + + def getData(self): + """!Function to get the data packages from server + must be polled by main program + + @return Next data packet.py from intern queue""" + if _clients: + message = _clients.pop() + logging.debug("Get data from queue") + return message + return None + + def flushData(self): + """!To flush all existing data in queue""" + logging.debug("Flush client data queue") + _clients.clear() diff --git a/boswatch/utils/paths.py b/boswatch/utils/paths.py new file mode 100644 index 0000000..08e54cb --- /dev/null +++ b/boswatch/utils/paths.py @@ -0,0 +1,39 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +"""! + ____ ____ ______ __ __ __ _____ + / __ )/ __ \/ ___/ | / /___ _/ /______/ /_ |__ / + / __ / / / /\__ \| | /| / / __ `/ __/ ___/ __ \ /_ < + / /_/ / /_/ /___/ /| |/ |/ / /_/ / /_/ /__/ / / / ___/ / +/_____/\____//____/ |__/|__/\__,_/\__/\___/_/ /_/ /____/ + German BOS Information Script + by Bastian Schroll + +@file: paths.py +@date: 07.01.2018 +@author: Bastian Schroll +@description: Important paths for some functions +""" +import logging +import os +import sys + + +logging.debug("- %s loaded", __name__) + +ROOT_PATH = os.path.dirname(sys.modules['__main__'].__file__) +LOG_PATH = ROOT_PATH + "/log/" +CONFIG_PATH = ROOT_PATH + "/config/" +CSV_PATH = ROOT_PATH + "/csv/" +BIN_PATH = ROOT_PATH + "/_bin/" + + +def createIfNotExist(dirPath): + """!Checks if an directory is existing and create it if not + + @param dirName: Path of the directory + @return Path of the directory""" + if not os.path.exists(dirPath): + os.mkdir(dirPath) + logging.debug("directory created: %s", dirPath) + return dirPath