pedroth
pedroth

Reputation: 657

Flask Socket.io doesn't work when emitting from thread

Description

The client side has 2 buttons:

This problem is a proxy of the real problem I am trying to solve.

I build the app, and in the server side it seems to work, but the client doesn't receive the server push, but is able to start the push and kill it!

What I tried

Server Side

import random
from threading import Thread
from time import sleep

from flask import Flask
from flask_socketio import SocketIO

SOCKET_NAMESPACE = '/test'

is_pushing = False

app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret!!'
socketio = SocketIO(app)


def server_push(fps):
    dt = 1 / fps
    global is_pushing
    while is_pushing:
        with app.test_request_context('/'):
            sent = f"Server Pushed!! {random.random()}"
            print(sent)
            socketio.emit("serverResponse", sent, namespace=SOCKET_NAMESPACE)
            sleep(dt)


@socketio.on('connect', namespace=SOCKET_NAMESPACE)
def on_connect():
    print("connected server!!")
    socketio.emit("serverResponse", "First Push", namespace=SOCKET_NAMESPACE)


@socketio.on('disconnect', namespace=SOCKET_NAMESPACE)
def on_disconnect():
    print("disconnected server!!")


@socketio.on('startServerPush', namespace=SOCKET_NAMESPACE)
def on_start_server_push(fps=1):
    print("Sever push start!!")
    global is_pushing
    is_pushing = True
    socketio.emit("serverResponse", "Start Push", namespace=SOCKET_NAMESPACE)
    Thread(target=lambda: server_push(fps)).start()


@socketio.on("killServerPush", namespace=SOCKET_NAMESPACE)
def on_kill_server_push():
    print("Server push stop!!")
    global is_pushing
    is_pushing = False
    socketio.emit("serverResponse", "Kill Push", namespace=SOCKET_NAMESPACE)


def main():
    socketio.run(app, port=8082, debug=True)


if __name__ == '__main__':
    main()

Client Side

import openSocket from 'socket.io-client';
import React, { Component } from 'react';

class Test extends Component {
  state = {
    pushedFromServer: [],
    socket: null
  };

  componentDidMount() {
    const url = 'localhost:8082/test';
    const socket = openSocket(url);
    socket.on('connect', () => console.log('Test connected!!'));
    socket.on('disconnect', () => console.log('Test disconnected!!'));
    socket.on('serverResponse', response => {
      console.log(response);
      const pushedFromServer = [...this.state.pushedFromServer];
      pushedFromServer.push(response);
      this.setState({ pushedFromServer });
    });
    this.setState({ socket });
  }

  killServerPush = () => {
    this.state.socket.emit('killServerPush');
  };

  startServerPush = () => {
    this.state.socket.emit('startServerPush');
  };

  render() {
    return (
      <div>
        <button onClick={this.startServerPush}>
          <h3>Start push from server</h3>
        </button>
        <button onClick={this.killServerPush}>
          <h3>Kill push from server</h3>
        </button>
        <ul>
          {this.state.pushedFromServer.map(value => (
            <li>{value}</li>
          ))}
        </ul>
      </div>
    );
  }
}

export default Test;

Final Notes

In the client, I could receive the First Push, and the Start Push, I am also able to stop the periodic process from the client and restart it. I am not able to receive the periodic messages on the client.

Thanks

Upvotes: 3

Views: 4656

Answers (1)

pedroth
pedroth

Reputation: 657

By looking at https://github.com/miguelgrinberg/python-socketio/issues/99, I found a solution to the issue.

Just need to change the server side.

Change line:

Thread(target=lambda: server_push(fps)).start()

to

 socketio.start_background_task(target=lambda: server_push(fps))

And instead of using python sleep, use:

socketio.sleep(dt)

Upvotes: 3

Related Questions