Flask socketio 不监听事件
Posted
技术标签:
【中文标题】Flask socketio 不监听事件【英文标题】:Flask socketio not listening to events 【发布时间】:2022-01-03 08:03:11 【问题描述】:我正在使用烧瓶 socketio 构建一个烧瓶应用程序。我在 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 连接升级成功,socket-io 服务器接收到来自客户端的消息,它只是没有将它传递给应该监听事件的函数。
澄清一下,我正在使用应用工厂模式运行烧瓶,侦听器代码位于蓝图views.py 文件中,遵循我的应用工厂代码和蓝图:
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
关于为什么会发生这种情况以及如何解决它的任何想法?
【问题讨论】:
【参考方案1】:您的客户端正在寻找socket.on('message', incoming_message_as_a_string)
,但您的服务器正在发送socket.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!');
);
原因是emit
可以发送对象或字符串,而send
只能发送字符串。
更新:
再看你的问题,我不认为你的两个方面是匹配的。发送套接字消息时,第一个参数是“事件名称” - 基本上是一个触发器,它可以让对方有一些需要注意的东西(以便套接字侦听器可以区分不同类型的消息)。
服务器只监听message
事件,然后发送自己的message
- 这意味着客户端必须先发送一些东西。通常,这是一条“连接”消息——当客户端连接时它会发送其中一条消息,但服务器端并未监听该事件。它只监听“消息”事件——客户端仅在第一次收到来自服务器的消息事件后发送message
事件。
在服务器端,尝试更改:
@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 - 我删除了命名空间的东西,因为没有它我也能正常工作,我不确定它会如何影响结果。进一步了解后,您可以将其重新添加。
【讨论】:
不幸的是,这不起作用,服务器仍然没有响应事件。我相信问题出在服务器代码上。 感谢您对使用 emit 的提醒 @JoãoA.Veiga 我用其他想法/建议更新了我的答案。 刚刚测试过,结果还是一样。服务器不会向客户端发送任何内容。我开始相信这可能是使用 eventlet 的 flask-socketio 服务器的更深层次的问题。有什么想法吗? 感谢@cssyphus 的洞察力,刚刚开始工作。我会发布一个答案来解释我做了什么【参考方案2】:不太确定这里的细节,但以下更改使服务器正常工作。
在最初的应用工厂函数中,我在注册蓝图之前调用了 socketio.init_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 更有经验的人可以更深入地研究这个问题,我们将不胜感激。
【讨论】:
以上是关于Flask socketio 不监听事件的主要内容,如果未能解决你的问题,请参考以下文章
托管在 Heroku 上的 Flask SocketIO 应用程序不使用 @socketio.on 事件?