Я пишу веб-приложение (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()