Сокет Python для сервера - PullRequest
0 голосов
/ 21 мая 2019

Я пишу веб-приложение (Flask), которое содержит TCP-сервер (ThreadingTCPServer), который получает удаленные подключения от клиентов.Я пытаюсь предложить клиентам ответить сообщением о состоянии по маршруту GET hello().В настоящее время, когда я печатаю содержимое current_app.tcp_server.clients в web.py, оно отображается как пустое;возможно, потому что это не экземпляр темы - я не знаю.Любая помощь будет принята с благодарностью.
Все файлы подробно описаны ниже.Я запускаю сервер, выполняя:

set FLASK_APP=web.py
flask run

И открываю клиентов с: python client.py

tcp.py

import sys
import time
import threading
from socketserver import BaseRequestHandler, ThreadingTCPServer

from logger import get_logger


class Server(ThreadingTCPServer):
    # Decides how threads will act upon termination of the main process
    daemon_threads = True
    # If true, server_close() waits until all non-daemonic threads terminate.
    block_on_close = True

    def __init__(self, *args, **kwargs):
        self.logger = get_logger(self.__class__.__name__)
        self._threads = []
        self.clients = []
        super().__init__(*args, **kwargs)
        self.logger.info(f'Started server on {self.server_address}')

    def add_thread(self, func, *args):
        """Configure thread and add to self in order to manage termination."""
        t = threading.Thread(target=func, args=args)
        t.daemon = self.daemon_threads
        if not t.daemon and self.block_on_close:
            self._threads.append(t)
        t.start()

    def status_all(self):
        for client in self.clients:
            print(client.request)
            client.get_status()


class RequestHandler(BaseRequestHandler):
    def __init__(self, *args, **kwargs):
        self.logger = get_logger(self.__class__.__name__)
        self.status = None
        self.hooks = {
            'STATUS': self._set_status
        }
        super().__init__(*args, **kwargs)
        self.server.clients.append(self)

    def setup(self):
        self.logger.info(f'Connection received: {self.client_address}')
        self.get_status()

    def handle(self):
        while True:
            self.recv = self.request.recv(1024).decode().strip()
            self.logger.info(f'Received <{self.client_address}>: {self.recv}')
            args = self.recv.strip().split(' ')
            if args[0] in self.hooks:
                self.logger.debug(f'{args[0]} hook called')
                if len(args) > 1:
                    self.hooks[args[0]](args[1:])
                else:
                    self.hooks[args[0]]()

    def send(self, msg):
        self.logger.info(f'Sending <{self.client_address}>: {msg}')
        self.request.sendall(msg.encode())

    def get_status(self):
        self.send('STATUS')

    def _set_status(self, *args):
        self.status = ' '.join(*args)

web.py

import threading
from flask import Flask, current_app
from tcp import Server, RequestHandler

app = Flask(__name__)
app.tcp_server = Server(('localhost', 4444), RequestHandler)
try:
    app.tcp_thread = threading.Thread(target=app.tcp_server.serve_forever)
    app.tcp_thread.start()
except Exception as e:
    print(e)

@app.route("/")
def hello():
    print(current_app.tcp_server.clients)
    current_app.tcp_server.status_all()
    return '\n'.join([client.status for client in current_app.tcp_server.clients])

logger.py

import sys
import logging
from logging.handlers import RotatingFileHandler

LOG_LEVEL = 'DEBUG'
LOG_FILE = 'log.txt'
LOG_FILE_COUNT = 3
LOG_CONSOLE_FORMAT = "%(name)s:%(levelname)s: %(message)s"
LOG_FILE_FORMAT = "%(asctime)s %(name)s[%(process)d]: %(levelname)s - %(message)s"


def get_logger(name):
    logger = logging.getLogger(name)
    logger.setLevel(LOG_LEVEL)

    simple_formatter = logging.Formatter(LOG_CONSOLE_FORMAT)
    detailed_formatter = logging.Formatter(LOG_FILE_FORMAT)

    console_log_handler = logging.StreamHandler(sys.stdout)
    console_log_handler.setLevel(LOG_LEVEL)
    console_log_handler.setFormatter(simple_formatter)

    file_log_handler = RotatingFileHandler(LOG_FILE, backupCount=LOG_FILE_COUNT)
    file_log_handler.setLevel(LOG_LEVEL)
    file_log_handler.setFormatter(detailed_formatter)

    logger.addHandler(console_log_handler)
    logger.addHandler(file_log_handler)
    return logger

client.py

import sys
from logger import get_logger
from socket import socket, AF_INET, SOCK_STREAM


class Client:
    def __init__(self, host='localhost', port=4444):
        self.logger = get_logger(self.__class__.__name__)
        self.host = host
        self.port = port
        self.status = 'IDLE'
        self.hooks = {
            'STATUS': self._send_status
        }
        self.run()

    def connect(self, host, port):
        try:
            self.logger.info(f'Attempting to connect to {self.host}:{self.port}...')
            self.sock = socket(AF_INET, SOCK_STREAM)
            self.sock.connect((self.host, self.port))
            self.logger.info(f'Connection established.')
        except:
            self.logger.error(f'Unable to connect to {host}:{port}')
            raise ConnectionError(f'Unable to connect to {host}:{port}')

    def disconnect(self):
        try:
            self.socket.close()
            self.logger.info('Disconnected.')
        except:
            pass

    def run(self):
        self.connect(self.host, self.port)
        try:
            while True:
                self.recv = self.sock.recv(1024).decode()
                if self.recv:
                    self.logger.info(f'Received: {self.recv}')
                args = self.recv.strip().split(' ')

                if args[0] in self.hooks:
                    self.logger.debug(f'{args[0]} hook called')
                    if len(args) > 1:
                        self.hooks[args[0]](args[1:])
                    else:
                        self.hooks[args[0]]()
        finally:
            self.logger.info('Closing connection.')
            self.disconnect()

    def send(self, msg):
        self.logger.info(f'Sending: {msg}')
        self.sock.send(msg.encode())

    def _send_status(self):
        self.send(f'STATUS {self.status}')

    def __del__(self):
        self.disconnect()


if __name__ == '__main__':
    logger = get_logger(__name__)
    try:
        logger.debug('Starting main loop...')
        c = Client()
    except KeyboardInterrupt:
        logger.info('Exiting!')
        sys.exit()
...