当前位置: 首页 > 知识库问答 >
问题:

Flask socketio不倾听事件

邹坚壁
2023-03-14

我正在利用flask socketio构建一个flask应用程序。我在Ubuntu 20.04上本地运行它,在localhost:8000上运行gunicorn和eventlet。

我的服务器端测试代码如下所示:

$(document).ready(function() {
    // const socketGame = io(window.location.hostname + "/coin")
    var socket = io.connect('http://127.0.0.1:8000');

    socket.on('connect', function() {
        console.log("ok");
        socket.send('User has connected!');
    });

    socket.on('message', function(msg) {
        console.log('Received message');
    });

});

在我的服务器中,我有以下侦听器:

import re

from flask import render_template, request, redirect, url_for, jsonify, current_app
from flask_mail import Mail, Message
from flask_socketio import emit, join_room, leave_room, rooms
from flask_login import LoginManager, UserMixin, login_user, login_required, current_user, logout_user
from werkzeug.security import generate_password_hash, check_password_hash

from .. import socketio
from . import games_blueprint
from app.models import *
from app.utilities import sanitizeHtml

#socketio chat server
#need to create event for admin broadcasting messages
@socketio.on('message', namespace='/')
def handleMessage(msg):
    print('Message: ' + msg)
    send(msg, broadcast=True)

它只需在终端中打印来自客户端的消息并广播回消息。但是,从gunicorn日志来看,服务器似乎正在从客户端接收事件,而不是将其传递给侦听器:

(vegazbetenv) jveiga@LAPTOP-C6PJKMM6:~/vegazbet$ gunicorn --worker-class eventlet -w 1 wsgi:application
[2021-11-24 21:00:59 -0300] [134633] [INFO] Starting gunicorn 20.1.0
[2021-11-24 21:00:59 -0300] [134633] [INFO] Listening at: http://127.0.0.1:8000 (134633)
[2021-11-24 21:00:59 -0300] [134633] [INFO] Using worker: eventlet
[2021-11-24 21:00:59 -0300] [134635] [INFO] Booting worker with pid: 134635
Server initialized for eventlet.
Server initialized for eventlet.
Server initialized for eventlet.
HYYx0QNgrNr7YodDAAAA: Sending packet OPEN data {'sid': 'HYYx0QNgrNr7YodDAAAA', 'upgrades': ['websocket'], 'pingTimeout': 20000, 'pingInterval': 25000}
HYYx0QNgrNr7YodDAAAA: Received packet MESSAGE data 0
HYYx0QNgrNr7YodDAAAA: Sending packet MESSAGE data 0{"sid":"q1NjSbTlPRYmT_TYAAAB"}
HYYx0QNgrNr7YodDAAAA: Received request to upgrade to websocket
HYYx0QNgrNr7YodDAAAA: Received packet MESSAGE data 2["message","User has connected!"]
received event "message" from q1NjSbTlPRYmT_TYAAAB [/]
HYYx0QNgrNr7YodDAAAA: Upgrade to websocket successful
HYYx0QNgrNr7YodDAAAA: Sending packet PING data None
HYYx0QNgrNr7YodDAAAA: Received packet PONG data 

从日志中可以看出,websocket连接已成功升级,并且ocock-io服务器接收到来自客户端的消息,它只是没有将其传递给应该侦听事件的函数。

为了澄清一下,我正在使用应用程序工厂模式运行flask,侦听器代码在蓝图视图中。py文件,遵循我的app factory代码和蓝图:

app/__init__.py

import os

from flask import Flask, jsonify, render_template, request, redirect, url_for
from flask_login import LoginManager, UserMixin, login_user, login_required, current_user, logout_user
from flask_mail import Mail
from flask_socketio import SocketIO
from flask_mongoengine import MongoEngine
from flask_wtf.csrf import CSRFProtect
from celery import Celery


from config import Config
from app.models import *
from config import *


### Flask extension objects instantiation ###
mail = Mail()
csrf = CSRFProtect()
login_manager = LoginManager()
# socketio = SocketIO(engineio_logger=True, logger=True, message_queue=os.getenv('CELERY_BROKER_URL'), cors_allowed_origins="*")
socketio = SocketIO(engineio_logger=True, logger=True, cors_allowed_origins="*")

### Celery instantiation ###
celery = Celery(__name__, broker=Config.CELERY_BROKER_URL, result_backend=Config.RESULT_BACKEND)


### Application Factory ###
def create_app():

    app = Flask(__name__)

    # Configure the flask app instance
    CONFIG_TYPE = os.getenv('CONFIG_TYPE', default='config.DevelopmentConfig')
    app.config.from_object(CONFIG_TYPE)

    #Configure celery
    celery.conf.update(app.config)

    # Initialize flask extension objects
    initialize_extensions(app)

    # Register blueprints
    register_blueprints(app)

    #setup db integration
    configure_db(app)

    # Configure logging
    configure_logging(app)

    # Register error handlers
    register_error_handlers(app)

    #setup db for flask-login
    @login_manager.user_loader
    def load_user(user_id):
        user = User.objects(id=user_id)[0]
        return user

    return app


### Helper Functions ###
def register_blueprints(app):
    from app.auth import auth_blueprint
    from app.main import main_blueprint
    from app.games import games_blueprint
    from app.admin import admin_blueprint

    app.register_blueprint(auth_blueprint, url_prefix='/users')
    app.register_blueprint(main_blueprint)
    app.register_blueprint(games_blueprint, url_prefix='/games')
    app.register_blueprint(admin_blueprint, url_prefix='/admin')

def initialize_extensions(app):
    mail.init_app(app)
    login_manager.init_app(app)
    csrf.init_app(app)
    socketio.init_app(app)

def register_error_handlers(app):

    # 400 - Bad Request
    @app.errorhandler(400)
    def bad_request(e):
        return render_template('400.html'), 400

    # 401 - Unauthorized
    @app.errorhandler(401)
    def bad_request(e):
        return render_template('401.html'), 401

    # 403 - Forbidden
    @app.errorhandler(403)
    def forbidden(e):
        return render_template('403.html'), 403

    # 404 - Page Not Found
    @app.errorhandler(404)
    def page_not_found(e):
        return render_template('404.html'), 404

    # 405 - Method Not Allowed
    @app.errorhandler(405)
    def method_not_allowed(e):
        return render_template('405.html'), 405

    # 500 - Internal Server Error
    @app.errorhandler(500)
    def server_error(e):
        return render_template('500.html'), 500

def configure_logging(app):
    import logging
    from flask.logging import default_handler
    from logging.handlers import RotatingFileHandler

    # Deactivate the default flask logger so that log messages don't get duplicated
    app.logger.removeHandler(default_handler)

    # Create a file handler object
    file_handler = RotatingFileHandler('flaskapp.log', maxBytes=16384, backupCount=20)

    # Set the logging level of the file handler object so that it logs INFO and up
    file_handler.setLevel(logging.INFO)

    # Create a file formatter object
    file_formatter = logging.Formatter('%(asctime)s %(levelname)s: %(message)s [in %(filename)s: %(lineno)d]')

    # Apply the file formatter object to the file handler object
    file_handler.setFormatter(file_formatter)

    # Add file handler object to the logger
    app.logger.addHandler(file_handler)

def configure_db(app):
    db = MongoEngine(app)

app/games/__init__.py

#This bluepint will deal with all site functionality

from flask import Blueprint

games_blueprint = Blueprint('games', __name__, template_folder='templates')

from . import views

有没有想过为什么会发生这种情况以及如何解决?

共有2个答案

葛哲彦
2023-03-14

不太确定这里的细节,但以下更改使服务器正常工作。

在最初的应用程序工厂函数中,我调用了socketio。在注册蓝图之前初始化app(app)。我刚刚将其移动到应用程序工厂中的最后一个操作:

def create_app():

    app = Flask(__name__)

    # Configure the flask app instance
    CONFIG_TYPE = os.getenv('CONFIG_TYPE', default='config.DevelopmentConfig')
    app.config.from_object(CONFIG_TYPE)

    #Configure celery
    celery.conf.update(app.config)

    # Register blueprints
    register_blueprints(app)

    #setup db integration
    configure_db(app)

    # Configure logging
    configure_logging(app)

    # Register error handlers
    register_error_handlers(app)
    
    ##########################################
    # HERE
    # Initialize flask extension objects
    initialize_extensions(app)
    ##########################################

    #setup db for flask-login
    @login_manager.user_loader
    def load_user(user_id):
        user = User.objects(id=user_id)[0]
        return user

    return app

我猜想操作顺序在某种程度上阻止了socketio实际注册事件处理程序。如果任何对flask socketio更有经验的人能够深入了解这个问题,我们将不胜感激。

应翰飞
2023-03-14

您的客户正在寻找套接字。在('message',incoming_message_as_a_string)上,但您的服务器正在发送套接字。send('just the string without an event')

尝试:

socket.on('connect', function() {
    socket.send('message', 'User has connected!');
});

但是我会养成在客户端和服务器上始终使用emit的习惯:

socket.on('connect', function() {
    console.log("ok");
    socket.emit('message', 'User has connected!');
});

原因是发射可以同时发送对象或字符串,而发送只能发送字符串。

再看看你的问题,我认为你的双方并不匹配。当发送套接字消息时,第一个参数是“事件名称”-基本上是一个触发器,它为另一端提供了一些需要注意的内容(以便套接字侦听器可以区分不同类型的消息)。

服务器只侦听消息事件,然后发送自己的消息,这意味着客户端必须先发送一些东西。通常,这是一条“连接”消息——当客户端连接时,它确实会发送其中一条消息,但服务器端没有侦听该事件。它只监听“消息”事件,客户端只在第一次从服务器收到消息事件后发送消息事件。

在服务器端,尝试更改以下内容:

@socketio.on('message', namespace='/')
def handleMessage(msg):
    print('Message: ' + msg)
    send(msg, broadcast=True)

为此:

@socketio.on('connect')
def handleConnect():
    print('Someone connected')
    send('message', 'A user has joined', broadcast=True)

@socketio.on('message')
def handleMessage(msg):
    print('Message: ' + msg)
    send(msg, broadcast=True)

PS-我删除了名称空间的东西,因为我在没有它的情况下工作,我不确定它会如何影响结果。一旦你做得更进一步,你可以把它加回去。

 类似资料:
  • Blade 中提供一个方法帮助开发者可以自定义的监听应用程序运行中的一些生命周期。比如 Session 的创建与销毁,应用启动结束后等。 支持的事件类型有如下: public enum EventType { SERVER_STARTING, // 服务准备启动 SERVER_STARTED, // 服务启动成功 SERVER_STOPPING, //

  • Nutz.Ioc 容器有三种事件: 对象被创建(create事件) 对象被从容器中取出(fetch事件) 对象被销毁(depose事件) 在这三种时刻,你如果想做一些特殊的操作,比如,当一个数据源被销毁时,你希望能够关闭所有的连接, 声明一下,你想监听什么事件,以及怎么监听。 注: 如果你的对象是 "singleton: false",那么容器创建了对象后就会立即忘记它的存在。因为鬼才知道 你打算

  • 全局事件 事件监听 注解监听 以imi/src/Listener/Init.php为例 <?php namespace Imi\Listener; use Imi\Event\EventParam; use Imi\Event\IEventListener; use Imi\Bean\Annotation\Listener; /** * @Listener(eventName="IMI.IN

  • Chrome DevTools命令行API提供了多种方式来观察和检查事件监听器。JavaScript在交互式页面中起着中心作用,并且浏览器为您提供了一些有用的工具来调试事件和事件处理程序。 TL;DR 使用monitorEvents()监听某一类型的事件。 使用unmonitorEvents()停止监听。 使用getEventListeners()获取DOM元素的监听器。 使用Event List

  • sTree触发容器上的各种事件。您可以查看所有事件的列表以了解要听的内容。 要获取有关事件的更多信息,请检查其data参数。 在大多数情况下,涉及节点的情况下,您将传递整个节点对象。如果在某处获取ID字符串并想要检查该节点,则只需使用 .get_node()。内部节点对象与用于加载的JSON格式非常相似,但是具有一些额外的属性,这可能很有用:children是节点的直接子节点的children_d

  • 我们正在开发一个应用程序,我们想听Kafka中不止一个主题。所有主题都有一个分区。所有主题名称都有一个公共的前缀,例如“test-x”、“test-y”,所以我们可以对它使用spring。 我们希望编写一个java spring使用者,它使用模式监听所有主题。我们的想法是,我们可以运行同一个消费者(属于同一个组)的多个实例,Kafka将为不同的消费者分发来自不同主题的消息。 然而,这似乎并不奏效。