I would like to build an application to monitor files on my local computer through the browser. I have written a minimalistic example to illustrate this.
The frontend should be able to request messages from the backend on events. In my example below, I am calling these "standard messages". They are triggered in the frontend by a button click and a fetch
call, and handled in the backend by a route
.
The backend should also send messages every 5 seconds. I am calling these "continuous messages" and I am trying to send them using Flask-SocketIO. They are sent by the backend with socketio start_background_task
and emit
, and are received by the frontend with socket.on()
.
The "standard messages" part works fine if I comment the socketio part (and replace socketio.run
with app.run
). But when I uncomment the socketio part to have "continuous messages", the communication fails. The backend doesn't receive the "standard messages" calls, and the frontend doesn't receive the "continuous messages".
It looks like I am not writing the socketio part correctly. What do I need to add so that both messages type work correctly?
Backend
import time
import logging
from flask import Flask
from flask_cors import CORS
from flask_socketio import SocketIO
app = Flask(__name__)
CORS(app)
logging.basicConfig(level=logging.DEBUG)
# Continuous messages ##########################################
socketio = SocketIO(app, cors_allowed_origins="*", logger=True, engineio_logger=True)
@socketio.on('connect')
def handle_connect():
print('Client connected')
socketio.start_background_task(send_continuous_messages)
def send_continuous_messages():
while True:
print("Backend sends a continuous message")
socketio.emit("continuous_message", {"data": "New continuous message"})
time.sleep(5)
################################################################
@app.route("/standard-message", methods=["GET"])
def generate_standard_message():
print("Backend sends a standard message")
return {"data": "New standard message"}
if __name__ == "__main__":
print("Starting app")
socketio.run(app, debug=True)
Frontend
const myButton = document.getElementById('my-button');
const BASE_URL = "http://localhost:5000";
localStorage.debug = '*';
// Continuous messages #########################################
const socket = io(BASE_URL);
socket.on('connect', () => {
console.log('WebSocket connected');
});
socket.on('disconnect', () => {
console.log('WebSocket disconnected');
});
socket.on('continuous_message', (data) => {
console.log('Frontend received a continuous message:', data);
});
// #############################################################
async function receiveStandardMessage() {
console.log('Frontend is requesting a standard message');
const response = await fetch(`${BASE_URL}/standard-message`);
const data = await response.json();
console.log('Frontend received a standard message:', data);
}
myButton.addEventListener('click', receiveStandardMessage);
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>My app</title>
<script src="https://cdn.socket.io/4.0.0/socket.io.min.js"></script>
</head>
<body>
<div id="app">
<button id="my-button">Send standard message</button>
</div>
<script src="app.js"></script>
</body>
</html>
Server logs
Server initialized for eventlet.
INFO:engineio.server:Server initialized for eventlet.
Starting app
INFO:werkzeug: * Restarting with stat
Server initialized for eventlet.
INFO:engineio.server:Server initialized for eventlet.
Starting app
WARNING:werkzeug: * Debugger is active!
INFO:werkzeug: * Debugger PIN: 141-243-050
(6318) wsgi starting up on http://127.0.0.1:5000
(6318) accepted ('127.0.0.1', 52009)
GotWW8oC3BEz4J3jAAAA: Sending packet OPEN data {'sid': 'GotWW8oC3BEz4J3jAAAA', 'upgrades': ['websocket'], 'pingTimeout': 20000, 'pingInterval': 25000, 'maxPayload': 1000000}
INFO:engineio.server:GotWW8oC3BEz4J3jAAAA: Sending packet OPEN data {'sid': 'GotWW8oC3BEz4J3jAAAA', 'upgrades': ['websocket'], 'pingTimeout': 20000, 'pingInterval': 25000, 'maxPayload': 1000000}
127.0.0.1 - - [06/Mar/2025 17:40:49] "GET /socket.io/?EIO=4&transport=polling&t=PLiLCOB HTTP/1.1" 200 335 0.001486
GotWW8oC3BEz4J3jAAAA: Received packet MESSAGE data 0
INFO:engineio.server:GotWW8oC3BEz4J3jAAAA: Received packet MESSAGE data 0
Client connected
GotWW8oC3BEz4J3jAAAA: Sending packet MESSAGE data 0{"sid":"w_DlD83bH5OBQRbiAAAB"}
INFO:engineio.server:GotWW8oC3BEz4J3jAAAA: Sending packet MESSAGE data 0{"sid":"w_DlD83bH5OBQRbiAAAB"}
127.0.0.1 - - [06/Mar/2025 17:40:50] "POST /socket.io/?EIO=4&transport=polling&t=PLiLCOy&sid=GotWW8oC3BEz4J3jAAAA HTTP/1.1" 200 202 0.003777
Backend sends a continuous message
emitting event "continuous_message" to all [/]
INFO:socketio.server:emitting event "continuous_message" to all [/]
GotWW8oC3BEz4J3jAAAA: Sending packet MESSAGE data 2["continuous_message",{"data":"New continuous message"}]
INFO:engineio.server:GotWW8oC3BEz4J3jAAAA: Sending packet MESSAGE data 2["continuous_message",{"data":"New continuous message"}]
Backend sends a continuous message
emitting event "continuous_message" to all [/]
INFO:socketio.server:emitting event "continuous_message" to all [/]
GotWW8oC3BEz4J3jAAAA: Sending packet MESSAGE data 2["continuous_message",{"data":"New continuous message"}]
INFO:engineio.server:GotWW8oC3BEz4J3jAAAA: Sending packet MESSAGE data 2["continuous_message",{"data":"New continuous message"}]
Client logs
Frontend is requesting a standard message
Firefox can’t establish a connection to the server at ws://localhost:5000/socket.io/?EIO=4&transport=websocket&sid=GotWW8oC3BEz4J3jAAAA.
WebSocket disconnected
The server logs show two background messages that were sent. There are no errors in this log.
The client log shows a failure to connect over WebSocket, which means that the connection was based on HTTP long polling. I'm not really sure about the WebSocket failure, but considering you are using eventlet and this library hasn't been maintained in a long time you should drop it.
Long time ago Flask-SocketIO needed eventlet or gevent to support WebSocket, but with current versions they are not needed anymore. You can have a WebSocket enabled Flask-SocketIO server using the Flask dev server during development and switching to Gunicorn with the threaded worker for production.