Flask-websocket Emit主动发送消息失败

现在在用flask实现一个简单的webserver,为了与前段进行通信,加入了flask-socketio模块,现在在处理前段发送过来的消息的函数中直接emit的话,前端可以收到消息。但是想主动发送消息的话失败。具体如下:

在这边的话前端是可以接收到emit发送的消息的。

@socketio.on('request_for_response')
def handle_request(request):
    print('received socket message:' + json.dumps(request))
    # 这里可以直接发送,如果单独发送就不可以?
    emit('response', 'ok')
    msgTranslate(request)

如果在普通函数里直接调用就会失败,这里的emit始终不能发送消息。

def recvThread():
    while True:
        # print("recvThread start---")
        msgNum = c_long(0)
        content = create_string_buffer(1024)
        if recvMsg(byref(msgNum), content) == 0:
            print(content)
            print("the content number is %d, the content is:%s" % (msgNum.value, content.value))
            # 必须要全局的才能调用
            d = dict(name='djj', age=26)
            print(json.dumps(d))
            # response(json.dumps(d))
            socketio.emit('response')

socketio定义如下

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

前端代码如下:

 var $SCRIPT_ROOT = {{ request.script_root|tojson|safe }};
 var socket = io.connect($SCRIPT_ROOT);
                socket.on('connect',function()
                    {
                       socket.emit('connected');
                    });
                 socket.on('response', function(data)
                  {
                      console.log(data);
                      alert("receive response");
                      /*
                      if(data.code == '200'){
                          alert(data.param);
                      }
                      else
                      {
                          alert('Error:'+data.param);
                      }
                      */
                      //alert(data.param);

                  });

            $(document).ready(function(){
                $("#btn").click(function () {
                    socket.emit('request_for_response', JSON.stringify({
                            'ip': $("#ip").val(),
                            'cover': $("#cover").val(),
                            'gate': $("#gate").val(),
                    }));
                });
            });

发现一个问题:socketio.emit放在线程里就无法执行,我现在的启动的部分代码是这样的,

if __name__ == '__main__':
    loadDjjque()
    isInit = queInit()
    setCharacter(1)
    if isInit == 0:
        print("--queInit success--")
    t = threading.Thread(target=recvThread, name='recvThread')
    t.setDaemon(True)
    t.start()
    socketio.run(app, host='0.0.0.0', port=5555)

但是官方文档写的是:

但对于某些应用程序,服务器需要作为消息的发起者。 这可以用于向客户端发送在服务器中发起的事件的通知,例如在后台线程中。 可以使用socketio.send()和socketio.emit()方法向所有连接的客户端进行广播:

def some_function():

socketio.emit('some event', {'data': 42})

请注意,socketio.send()和socketio.emit()与上下文感知send()和emit()不同。 还要注意,在上面的用法中没有客户端上下文,所以broadcast=True,不需要指定。

作者:1994宅猫
链接:https://www.jianshu.com/p/411...
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

不知道为什么不可以发送出去??

阅读 9.8k
2 个回答

解决问题了,目前问题解决有两种方法!
1、在web发送回来的消息中 通过set_background_tsk()来开启新线程,但是要注意在这个线程中如果需要sleep,必须要用socketio.sleep()这样Flask才会放开消息循环,不然整个通信会阻塞。
代码如下:

app = Flask(__name__)
socketio = SocketIO(app, ansyc_mode=None)

@socketio.on("connect")
def connect():
    socketio.start_background_task(threadFunc)
    
def threadFunc():
    while(True):
        emit("response")
        socketio.sleep(3)

实际上这种方法还是同步的,整个流程的发起还是web,并没有实现正真意义上的互相通信。但是也可以解决现有问题。

2、使用app.app_context(),其实socketio.emit()没有发送成功是因为 没有找到app的上下文,使用with app.app_context()即可以找到app的上下文。但是要注意的是线程中的挂起方式还是得用socketio.sleep()
具体代码如下:

from eventlet.green import threading
app = Flask(__name__)
socketio = SocketIO(app, ansyc_mode=None)

def report():
    while True:
        with app.app_context():
            socketio.emit('response')
        socketio.sleep(5)

t = threading.Thread(target=report, name='report')
t.setDaemon(True)
t.start()
socketio.run(app, host='0.0.0.0', port=5555)

在这个方法中可以做到真正意义上的server端主动发起消息,建议采用后面一种方法

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏