How can I send data though socket-io without the client requesting first with python and flask

Question:

My goal is for my Flask server to send the client data either every three seconds, or when a function is called. To do this I am using SocketIO. However based on some example code I am working with, it seems that I can only send data after a client requests something. I don’t want the client to have to ‘poll’ to find if there is new data, so I want the server to push it when it is ready.

Here is what I tried so far. (some of the code is unnecessary since it is based off an example) This should use socketio to push the time to the client every few seconds.

HTML

<!DOCTYPE HTML>
<html>
<head>
    <title>Socket-Test</title>
    <script src="//code.jquery.com/jquery-1.12.4.min.js"></script>
    <script src="//cdnjs.cloudflare.com/ajax/libs/socket.io/2.2.0/socket.io.js"></script>
    <script type="text/javascript" charset="utf-8">
        $(document).ready(function() {

            namespace = '/test';
            var socket = io(namespace);

           

            socket.on('my_response', function(msg, cb) {
                $('#log').text( $('<div/>').text(msg.data).html());
                if (cb)
                    cb();
            });
            
            
            
        });
    </script>
</head>
<body style="background-color:white;">

    <h1 style="background-color:white;">Socket</h1>
    <div id="time" ></div>
</body>
</html>

Python

import threading
from flask import Flask, render_template, session, copy_current_request_context,request
from flask_socketio import SocketIO, emit, disconnect
from threading import Lock
import time

async_mode = None
app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret!'
socket_ = SocketIO(app, async_mode=async_mode)
thread = None
thread_lock = Lock()
clients = []
def update():
    time.sleep(1)
    emit('my_response',
         {'data': time.time},
         room=clients[0])
t=threading.Thread(target=update)



@socket_.on('connect')
def handle_connect():
    print('Client connected')
    clients.append(request.sid)
    t.start()


@app.route('/')
def index():
    
    return render_template('index.html', async_mode=socket_.async_mode)


@socket_.on('my_event', namespace='/test')
def test_message(message):
    session['receive_count'] = session.get('receive_count', 0) + 1
    emit('my_response',
         {'data': message['data'], 'count': session['receive_count']})


@socket_.on('my_broadcast_event', namespace='/test')
def test_broadcast_message(message):
    session['receive_count'] = session.get('receive_count', 0) + 1
    emit('my_response',
         {'data': time.time},
         broadcast=True)






socket_.run(app,port=8050)

I try to run it but it gives me the error RuntimeError: Working outside of request context.

Asked By: Alexander

||

Answers:

I fixed my code by following this tutorial: https://www.shanelynn.ie/asynchronous-updates-to-a-webpage-with-flask-and-socket-io/

import threading
from flask import Flask, render_template, session, copy_current_request_context,request
from flask_socketio import SocketIO, emit, disconnect
from threading import Lock
import time

async_mode = None
app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret!'
socket_ = SocketIO(app, async_mode=async_mode)
thread = None
thread_lock = Lock()

def update():
    
    time.sleep(1)
    socket_.emit('my_response',
         {'data': time.time()},
        namespace='/test')
    print("emitted")
    update()
t=threading.Thread(target=update)



@socket_.on('connect', namespace='/test')
def handle_connect():
    print('Client connected')
   
    if not t.isAlive():
        t.start()


@app.route('/')
def index():
    
    return render_template('index.html', async_mode=socket_.async_mode)


socket_.run(app,port=8070)

HTML

<!DOCTYPE HTML>
<html>
<head>
    <title>Socket-Test</title>
    <script src="//code.jquery.com/jquery-1.12.4.min.js"></script>
    <script src="//cdnjs.cloudflare.com/ajax/libs/socket.io/2.2.0/socket.io.js"></script>
    <script type="text/javascript" charset="utf-8">
        $(document).ready(function() {

            namespace = '/test';
            var socket = io(namespace);

           console.log(("test"));

            socket.on('my_response', function(msg) {
                $('#time').text( $('<div/>').text(msg.data).html());
                
                console.log(msg);
            });
            
            
            
        });
    </script>
</head>
<body style="background-color:white;">

    <h1 style="background-color:white;">Socket</h1>
    <div id="time" ></div>
</body>
</html>
Answered By: Alexander

I would like to point out that using recursion in this case is not the best choice.

you call the update function inside the update and do not have the completion of this process.

the best option would be to use a loop(as done in the link you attached)

def update():
    while True:
        time.sleep(1)
        socket_.emit('my_response', {'data': time.time()}, namespace='/test')
        print("emitted")
t=threading.Thread(target=update)

also, it would be better to write "while is_work_var" instead of "while True"

Answered By: v.kan