【问题标题】:flask-socketio emit message from backend to frontendflask-socketio 从后端向前端发出消息
【发布时间】:2022-02-10 00:35:16
【问题描述】:

我正在尝试使用源自 python 后端的 socket.io 将消息发送到前端。我能够注册前端和后端之间的初始连接,从前端向后端发送消息,从后端向前端回复,但不仅仅是从后端向前端发送消息.看起来很简单,但我什么也没看到。

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(在模板文件夹中)

<!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

当页面加载时,它会打印到标准输出:

  • 1:测试消息
  • 2:收到我的事件:{'data': 'User Connected'}

当页面加载时,它会打印到浏览器控制台:

  • 1:找到连接
  • 2:我的回复:你好

输出表明可以进行通信。此外,如果打开了第二个窗口,则浏览器控制台会显示message data: test message,这就是我只需要打开一个会话的情况。这似乎表明存在会话或命名空间问题,但我不确定出了什么问题。

【问题讨论】:

  • 我不确定它是否对你有帮助。在模板被渲染和交付之前,不会收到消息。第一个会话不是这种情况,因此尚未建立连接。但是,如果打开第二个会话,则第一个会话已建立、连接并接收消息。
  • @Detlef 嗯,这确实有帮助。如果会话处于活动状态,当打开一个新会话时,我会在其控制台中收到我正在寻找message data: test message 的消息。我相信每次刷新或更改页面时都会重新渲染模板,所以有没有办法在模板渲染后初始化会话?您认为问题出在 js 方面、python 方面还是两者兼而有之?

标签: python flask socket.io


【解决方案1】:

您是对的,每次刷新页面时都会重新渲染模板。这意味着旧会话也结束并创建了一个新会话。
问题出在这几行。
在呈现模板之前发出消息。这会导致上面提到的时序问题。

@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.

很遗憾,我无法从您的问题中看出您正在朝着哪个目标努力。

使用 websocket 本身有不同的方法。
一方面,Web 套接字启用对传入消息的回复或将它们广播给其他客户端。 handle_my_custom_event 显示了这种可能性。
另一种可能性是从后台向客户端发送消息。

我为此写了一个小例子。
在这种情况下,消息会定期从后台线程发出。客户端一连接,就检查线程是否已经启动。

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>

【讨论】: