3

I have a flask-socketio server that serves index that shows received messages on webpage. The server receives messages trough ZMQ or basic UDP in their own threads. In the same threads it emits the messages to webpage but only the ZMQ thread message are received. Can you tell me why doesn't the UDP thread eimtting work?

from flask import Flask, request
from flask_socketio import SocketIO, emit
from threading import Thread
import socket
import time, zmq, pmt

HTTP_PORT = 5000
ZMQ_PORT = 5001
UDP_IP = "127.0.0.1"
UDP_PORT = 5005

app = Flask(__name__, static_url_path="")
# app.config["SECRET_KEY"] = "secret!"
socketio = SocketIO(app)


def background_thread():
    sock = socket.socket(socket.AF_INET,  # Internet
                         socket.SOCK_DGRAM)  # UDP
    sock.bind((UDP_IP, UDP_PORT))

    while True:
        data, addr = sock.recvfrom(1024)  # buffer size is 1024 bytes
        message = 'hello'
        socketio.emit('gnu radio', (message,))
        time.sleep(0.10)
        print "received message:", data


def background_thread_2():
    # Establish ZMQ context and socket
    context = zmq.Context()
    socket = context.socket(zmq.SUB)
    socket.setsockopt(zmq.SUBSCRIBE, "")
    socket.connect("tcp://0.0.0.0:%d" % (ZMQ_PORT))

    while True:
        # Receive decoded ADS-B message from the decoder over ZMQ
        pdu_bin = socket.recv()
        pdu = str(pmt.deserialize_str(pdu_bin)).decode('utf-8', 'ignore').encode("utf-8")
        message = 'hello2'
        socketio.emit('gnu radio', (message,))
        time.sleep(0.10)


@app.route("/")
def index():
    return app.send_static_file("index.html")


@socketio.on("connect")
def connect():
    print("Client connected", request.sid)


@socketio.on("disconnect")
def disconnect():
    print("Client disconnected", request.sid)


if __name__ == "__main__":
    thread = Thread(target=background_thread)
    thread.daemon = True
    thread.start()
    thread = Thread(target=background_thread_2)
    thread.daemon = True
    thread.start()

    socketio.run(app, host="0.0.0.0", port=HTTP_PORT, debug=True)

Only Hello2 is received.

Ondra
  • 43
  • 5
  • Are you using gevent or eventlet by any chance? If you do, you need to monkey patch the python standard library so that it becomes non-blocking. – Miguel Grinberg Dec 28 '18 at 18:19
  • Nope. So far I haven't installed either even though while starting I get this message: WebSocket transport not available. Install eventlet or gevent and gevent-websocket for improved performance. Do you think installinng one of these and monkey patching will help? – Ondra Dec 29 '18 at 20:02
  • Any chance your thread is blocked on the `recvfrom(!024)` call? Are you sure data is being set on the socket? – Miguel Grinberg Dec 30 '18 at 18:32
  • Yeah I'm sure since the `print "received message:", data` actually prints the received message – Ondra Dec 31 '18 at 19:11

1 Answers1

1

The problem actually lies with Flask's reloader.

When you have debug mode enabled, by default, Flask will use a reloader - a process that monitors your app's file(s) and restarts the server when they change. Because of this, when Flask runs the socketio.run line and determines that debug mode should be enabled, it reruns the script.

This means that your background threads will be started again. But you already have a running thread that's bound to UDP 127.0.0.1:5005. The second attempt to do that will fail:

Exception in thread Thread-1:
Traceback (most recent call last):
  File "C:\Users\matejcik\AppData\Local\Programs\Python\Python37\lib\threading.py", line 917, in _bootstrap_inner
    self.run()
  File "C:\Users\matejcik\AppData\Local\Programs\Python\Python37\lib\threading.py", line 865, in run
    self._target(*self._args, **self._kwargs)
  File ".\app.py", line 23, in background_thread
    sock.bind((UDP_IP, UDP_PORT))
OSError: [WinError 10048] Only one usage of each socket address (protocol/network address/port) is normally permitted

The old UDP thread still exists, will receive the UDP messages and print them to console. But that thread is still referring to the old Python environment before restart, so the socketio instance is stale, sending messages to the void.

This is also the reason why ZMQ receives messages fine: because a ZMQ subscription is not exclusive, both the old and the new thread can start and the new thread will be able to relay messages to the new socketio without problem.

In general, interaction of threads and the reloader is messy, see also https://github.com/miguelgrinberg/Flask-SocketIO/issues/567

You can avoid the problem if you specify debug=False, or debug=True, use_reloader=False.

matejcik
  • 1,912
  • 16
  • 26