1) Процесс П, который обрабатывает некие данные (запускается в GUI).
2) Сервер Flask. Позволяет клиенту через запросы получить актуальные данные из П и внести изменения.
Т.к. сервер должен возвращать данные П, то между ними (двумя процессами) организован обмен через сокеты (не некоторому собственному протоколу).
Два процесса могут быть запущены независимо друг от друга, т.е сначала может быть запущен П, а затем уже сервер, или наоборот. Объединить два процесса в один нельзя. Из-за отсутствия последовательности запуска необходимо организовать “динамическое” соединение через сокет. Т.е., если П активен, а сервер запускается, то сервер подключается к сокету, открытому на П. Иначе, если П запускается, а сервер уже работает, то сервер будет пытаться подключиться к сокету П, пока ему это не удастся сделать (пока нет соединения с сокетом, сервер на любой из запросов будет возвращать статус 500).
Основные части кода
Процесс П включает в себя два метода (вызываются по кнопке в GUI):
process_start запускаются при запуске процесса;
process_stop запускается при остановке процесса.
from threading import Thread import socket import pickle from datetime import datetime def process_start(): global working working = True global SocketThread SocketThread = Thread(target=SocketThreadProc) # SocketThread.daemon = True SocketThread.start() def process_stop(): global working working = False sock.close() SocketThread.join() def SocketThreadProc(): global sock sock = socket.socket() sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.bind(('localhost', 9090)) sock.listen(1) global conn while working: try: sock.settimeout(1) conn, addr = sock.accept() conn.setblocking(1) decoder = PacketDecoder() now = str(datetime.now()) print "{}: {} connected.".format(now, ":".join(map(str, addr))) try: while working: data = conn.recv(1024) data = bytearray(data) decoder.feed(data) for packet in decoder.decode(): process_packet(packet) # except Exception as err: print 'Sub try error:', err finally: conn.close() now = str(datetime.now()) print "{}: {} closed.".format(now, ":".join(map(str, addr))) except socket.timeout as err: print err pass except socket.error as err: print err break def process_packet(packet): _, set_method, _ = PacketDecoder.parse_header(packet[:8]) data = pickle.loads(packet[8:]) if not set_method: # GET # разбор пакета, формирование пакета для ответа ... conn.send(resp_packet) # отправка пакета серверу else: # SET # разбор пакета, внесение изменений в данные процесса П ...
Два метода:
get вызывается каждые 2 сек (интервал задается). Формирует пакет, отправляет в сокет, получает ответ от процесса П и возвращает результат клиенту.
set вызывается при взаимодействии пользователя с формой (например, кнопка). Формирует пакет, отправляет серверу. Клиенту возвращает 200, если все хорошо.
@app.route('/get-data', methods=['POST']) def get(): global sock_connected if not sock_connected: socket_connection() return Response({'success': False}, 500, {'ContentType': 'application/json'}) # формирование пакета package ... try: sock.send(package) # прием ответа от процесса П data = bytearray(sock.recv(1024)) llength = list(data[:3]) length = (llength[0] << 16) | (llength[1] << 8) | llength[2] data = data[3:] while len(data) != length: data += bytearray(sock.recv(1024)) data = pickle.loads(data) res = dict() for d in data["data"]: res[d['name']] = d['value'] return json.dumps(res) except socket.error: # [10053] errno.WSAECONNABORTED стандартная ошибки sock.close() sock_connected = False return Response({'success': False}, 500, {'ContentType': 'application/json'}) @app.route('/set-data', methods=['POST']) def set(): global sock_connected if not sock_connected: socket_connection() return Response({'success': False}, 500, {'ContentType': 'application/json'}) # формирование пакета package ... try: sock.send(package) return Response({'success': True}, 200, {'ContentType': 'application/json'}) except socket.error: # [10053] errno.WSAECONNABORTED стандартная ошибки sock.close() sock_connected = False return Response({'success': False}, 500, {'ContentType': 'application/json'}) def socket_connection(): global sock global sock_connected sock_connected = False sock = socket.socket() try: sock.connect(('localhost', 9090)) sock_connected = True except socket.error as error: if error.errno == errno.ECONNREFUSED: print('Error: ECONNREFUSED') else: print(error) print 'STATUS SOCKET CONNECTED:', sock_connected if __name__ == '__main__': socket_connection() app.run(host='0.0.0.0')
Если изначально запущен процесс П, то при запуске сервера все тоже отлично.
Проблема возникает тогда, когда запущен процесс П, и я насколько раз запускаю/останавливаю сервер (выше я указал, что первый запуск проблем не вызывает). Не смотря на то, что серверу удается подключиться к сокету процесса П (это видно в логах: функция socket_connection выдает STATUS SOCKET CONNECTED: True), сервер работает некорректно: вызов get-data (каждые 2 сек) происходит, но не обрабатывается. В логах Flask не отображается, что пришел запрос get-data. В отладчике хрома (F12 - Network) все запросы get-data красные, а статус - “canceled”.
А вот если кликнуть на кнопку, то set-data нормально обрабатывается сервером. В логах Flask есть. Статус - 200.
Подскажите, пожалуйста, в чем может быть причина.