如何使用Flask-Socket IO从服务器向客户端发送消息 [英] How to send message from server to client using Flask-Socket IO

查看:81
本文介绍了如何使用Flask-Socket IO从服务器向客户端发送消息的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试创建一个可以从服务器向客户端发送消息的python应用程序.目前,我正在此处使用此示例代码.这是一个聊天应用程序,并且运行良好.我试图修改该应用程序,并在服务器端python代码中添加了一个新功能,该功能将在客户端中显示一条消息"Dummy",但似乎不起作用.

I'm trying to create a python app that can send message from server to client. Currently I'm using this sample code from here. It's a chat app and it's working fine. I tried to modified the app and add a new function in the server side python code that will print a message "Dummy" into the client but seems like it didn't work.

这是我的html代码:

Here's my html code:

index.html

<body>
    <ul id="messages"></ul>
    <ul id="output"></ul>
    <form action="">
      <input id="m" autocomplete="off" /><button>Send</button>
    </form>

<script src="{{url_for('static', filename='assets/vendor/socket.io.min.js')}}"></script>
<script src="{{url_for('static', filename='assets/vendor/jquery.js')}}"></script>

<script>
  var socket = io.connect('http://127.0.0.1:5000/chat');

  $('form').submit(function(){
    socket.emit('chat message', $('#m').val());
    $('#m').val('');
    return false;
  });

  socket.on('chat message', function(msg){
    $('#messages').html($('<li>').text(msg));
  });

  socket.on('output', function(msg){
    alert(msg)
    $('#messages').html($('<li>').text(msg));
  });
</script>

这是我的后端代码:

web_app.py

from flask import Flask
from flask import render_template
from flask_socketio import SocketIO
from flask_socketio import emit

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

def socket_onload(json):
    socketio.emit('output', str(json), namespace='/chat')
    print('received message: ' + str(json))


@socketio.on('chat message', namespace='/chat')
def handle_chat_message(json):
    print('received message: ' + str(json))
    emit('chat message', str(json), broadcast=True)


@socketio.on('connect')  # global namespace
def handle_connect():
    global connected
    connected = True
    print('Client connected')

@socketio.on('connect', namespace='/chat')
def handle_chat_connect():
    print('Client connected to chat namespace')
    emit('chat message', 'welcome!')

@socketio.on('disconnect', namespace='/chat')
def test_disconnect():
    print('Client disconnected')


@app.route('/')
def index():
    return render_template('index.html')


@app.route('/blah/')
def blah():
    return render_template('blah.html')

main.py

import web_app
import threading
import time

def main():
    import web_app
    webapp_thread = threading.Thread(target=run_web_app)
    webapp_thread.start()
    # webapp_thread = threading.Thread(target=run_web_app, args=(i,))

    while web_app.connected==False:
        print "waiting for client to connect"
        time.sleep(1)
        pass

    print "Connected..."
    time.sleep(3)
    print "Trying to print dummy message..."
    web_app.socket_onload("Dummy")

def run_web_app():
    web_app.socketio.run(web_app.app)

if __name__ == '__main__':
    main()

我可以在终端中看到收到的消息:虚拟",但在Web浏览器上没有任何变化.

I can see "received message: Dummy" in the terminal but nothing's change on the web browser.

推荐答案

您有两个错误,阻止您这样做:

You have two mistakes which prevent you from doing so:

首先,您正在尝试从套接字上下文外部发出一个带有socket.io的事件.

First, you are trying to emit an event with socket.io outside from the socket context.

当一个函数用 @ socketio.on 装饰器包装时,它成为一个 Event-Handlers .在服务器端触发事件时,它将搜索合适的处理程序来处理该事件,并将上下文初始化为发出该事件的特定客户端.

When a function is wraped with @socketio.on decorator, it becomes an Event-Handlers. While an event is fired on the server-side it will search for the right handler to handle the event and initialize the context to the specific client that emitted the event.

在没有初始化此上下文的情况下,您的 socketio.emit('output',str(json),namespace ='/chat')不会执行任何操作,因为服务器不知道应该与谁联系发出响应.

Without this context initializing, your socketio.emit('output', str(json), namespace='/chat') will do nothing because the server doesn't know to whom it should emit back the response.

无论如何,有一个小技巧可以手动特定客户端发送事件(即使您不在上下文中).每次打开套接字时,服务器都会将其分配给一个与套接字ID( sid )同名的私有"房间.因此,为了从客户端上下文向外部客户端发送消息,您可以创建一个已连接客户端ID的列表,并使用 room =< id> 自变量调用emit函数.

Anyway, there is a little trick for emitting events manually to a specific client (even if you are not in its context). Each time a socket has opened, the server assign it to a "private" room with the same name as the socket id (sid). So in order to send a message to the client outside from the client context, you can create a list of connected client's ids and call the emit function with the room=<id> argument.

例如:

web_app.py:

...
from flask import Flask, request

clients = []

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

@socketio.on('disconnect')
def handle_disconnect():
    print('Client disconnected')
    clients.remove(request.sid)

def send_message(client_id, data):
    socketio.emit('output', data, room=client_id)
    print('sending message "{}" to client "{}".'.format(data, client_id))

...

那么您可能会使用它如下:

Then you would probably use this as follow:

main.py:

import web_app
import threading
import time

def main():
    webapp_thread = threading.Thread(target=run_web_app)
    webapp_thread.start()

    while not web_app.clients:
        print "waiting for client to connect"
        time.sleep(1)

    print "Connected..."
    time.sleep(3)
    print "Trying to print dummy message..."
    web_app.send_message(web_app.clients[0], "Dummy")

...

但是,即使您尝试这样做,也无法正常工作(这使我们陷入第二个错误).

But even if you try this, it will not work (which brings us to the second mistake).

第二,您正在将eventlet与常规Python线程混合使用,这不是一个好主意.eventlet使用的绿色线程不适用于常规线程.相反,您应该使用绿色线程来满足所有线程需求.

Second, you are mixing eventlet with regular Python threads and it's not a good idea. the green threads that eventlet uses do not work well with regular threads. Instead, you should use green threads for all your threading needs.

我在Internet上找到的一个选择是猴子修补Python标准库,以便将线程,套接字等替换为对eventlet友好的版本.您可以在main.py脚本的最顶部执行此操作:

One option which I found in the internet, is to monkey patch the Python standard library, so that threading, sockets, etc. are replaced with eventlet friendly versions. You can do this at the very top of your main.py script:

import eventlet
eventlet.monkey_patch()

之后,它应该可以正常工作(我自己尝试过).让我知道您是否还有其他问题...

After that it should work fine (I tried it on my own). Let me know if you have another problems...

这篇关于如何使用Flask-Socket IO从服务器向客户端发送消息的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆