Reputation: 318
I am trying to emit a message using socket.io originating on a python backend to the frontend. I am able to register the initial connection between the front and the backend, to send a message from the frontend to the backend, and to reply from the backend to the frontend but not just send a message from the back to the front on its own. It seems rather simple but I am not seeing something.
main.py
from flask import Flask, render_template
from flask_socketio import SocketIO
app = Flask(__name__)
app.config['SECRET_KEY'] = 'tmp-secret-key'
socketio = SocketIO(app)
@app.route('/')
def sessions():
message = "test message"
print(message)
socketio.emit('message', message)
return render_template('index.html')
@socketio.on('my event')
def handle_my_custom_event(json, methods=['GET', 'POST']):
print('received my event: ' + str(json))
socketio.emit('my response', "hello")
if __name__ == '__main__':
socketio.run(app, debug=True)
index.html (in templates folder)
<!DOCTYPE html>
<html lang="en">
<head>
<title>Flask SocketIO Test</title>
</head>
<body>
<script src="https://cdn.socket.io/4.4.1/socket.io.min.js" integrity="sha384-fKnu0iswBIqkjxrhQCTZ7qlLHOFEgNkRmK2vaO/LbTZSXdJfAu6ewRBdwHPhBo/H" crossorigin="anonymous"></script>
<script type="text/javascript">
var socket = io();
socket.on('connect', function() {
console.log("connection found")
socket.emit( 'my event', {
data: 'User Connected'
});
});
socket.on('message', function(data) {
console.log("message data: ", data);
});
socket.on('my response', function(msg) {
console.log("my response: ", msg)
})
</script>
</body>
</html>
requirements.txt
flask==2.0.2
flask-socketio==5.1.1
simple-websocket==0.5.0
python-engineio==4.3.1
python-socketio==5.5.1
when page loads this is printed to stdout:
when page loads this is printed to the browser console:
The outputs show that communication is possible. Also if a second window is open then the browser console shows message data: test message
which is what I am looking for just with only one session open. This seems to indicate a session or namespace issue but I am not sure what is wrong.
Upvotes: 2
Views: 3714
Reputation: 8582
You are correct, the template is re-rendered every time the page is refreshed. This means that the old session is also ended and a new one is created.
The problem arises from these lines.
The message is emitted before the template is rendered. This leads to the mentioned timing problem.
@app.route('/')
def sessions():
message = "test message"
print(message)
socketio.emit('message', message) # The message is emitted.
return render_template('index.html') # The template is rendered and sent.
Unfortunately, I can't tell from your question which goal you are working towards.
There are different ways of working with websockets per se.
On the one hand, web sockets enable the reply to incoming messages or broadcast them to other clients. handle_my_custom_event
shows this possibility.
Another possibility is to send messages to the client from the background.
I wrote a small example for this.
In this case, messages are periodically emitted from a background thread. As soon as a client is connected, it is checked whether the thread has already been started.
from threading import Lock
from flask import Flask, render_template
from flask_socketio import SocketIO, emit
app = Flask(__name__)
app.secret_key = 'your secret here'
sio = SocketIO(app)
thread = None
thread_lock = Lock()
@app.route('/')
def index():
return render_template('index.html')
def background_task():
while True:
sio.sleep(10)
sio.emit('my_response', { 'data': 'a new message' })
@sio.on('connect')
def connect():
global thread
with thread_lock:
if thread is None:
thread = sio.start_background_task(background_task)
emit('my_response', { 'data': 'connected' })
if __name__ == '__main__':
sio.run(app)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Index</title>
</head>
<body>
<script src="https://cdn.socket.io/4.4.1/socket.io.min.js" integrity="sha384-fKnu0iswBIqkjxrhQCTZ7qlLHOFEgNkRmK2vaO/LbTZSXdJfAu6ewRBdwHPhBo/H" crossorigin="anonymous"></script>
<script type="text/javascript">
(() => {
const sock = io();
sock.on('my_response', data => {
console.log(data);
});
})();
</script>
</body>
</html>
Upvotes: 2