You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flask+SocketIO问题:无法推送外部设备WebSocket消息至异步HTML页面

Flask SocketIO 应用问题排查与修复

问题概述

  • 外部设备(IP:10.0.0.144)向部署在10.0.0.218:8089的应用发送WebSocket消息时,控制台返回400错误(请求语法错误);
  • 收到的消息无法推送到异步HTML页面,页面无更新。

消息已存入列表,仅关注来自10.0.0.144的消息,不确定当前实现是否最优。


相关代码

Flask 后端代码

from flask import Flask, render_template
from flask_socketio import *
from threading import Thread, Event
import socket
import json

app = Flask(__name__)
socketio = SocketIO(app)

# 存储消息的共享变量
messages = []

thread = Thread()
thread_stop_event = Event()

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

@socketio.on('connect', namespace='/test')
def test_connect():
    # 需要全局线程对象的可见性
    global thread
    print('客户端已连接')

    # 仅在线程未启动时启动消息推送线程
    if not thread.is_alive():
        print("启动线程")
        thread = socketio.start_background_task(start_socket_server)

def start_socket_server(host='10.0.0.218', port=8089):
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    server_socket.bind((host, port))
    server_socket.listen(5)
    print(f"服务器监听 {host}:{port}")

    while True:
        client_socket, addr = server_socket.accept()
        message = client_socket.recv(1024).decode()
        if message:
            # print(f"{addr, message}")
            messages.append(message)
            # 向所有连接的客户端发送消息
            socketio.emit('new_message', {'message': message})
        client_socket.close()

if __name__ == "__main__":
    # 在独立线程中启动socket服务器
    socket_thread = Thread(target=start_socket_server)
    socket_thread.start()

    # 启动带SocketIO的Flask应用
    socketio.run(app, allow_unsafe_werkzeug=True)

前端JS代码

$(document).ready(function(){
    // 连接socket服务器
    var socket = io.connect()
    var messages_received = [];

    // 接收服务器消息
    socket.on('new_message', function(msg) {
        console.log("收到消息" + msg.number);
        // 仅保留最近10条消息
        if (messages_received.length >= 10){
            messages_received.shift()
        }
        messages_received.push(msg.number);
        messages_string = '';
        for (var i = 0; i < messages_received.length; i++){
                messages_string += '<p>' + messages_received[i].toString() + '</p>';
        }
        $('#log').html(messages_string);
    });

});

控制台输出示例

* Running on http://10.0.0.218:8089
Press CTRL+C to quit

* Restarting with stat
  Server initialized for threading.
  Werkzeug appears to be used in a production deployment. Consider switching to a production web server instead.

* Debugger is active!

* Debugger PIN: 123-630-169
  RMoYImBp0_FvWs_lAAAA: Sending packet OPEN data {'sid': 'RMoYImBp0_FvWs_lAAAA', 'upgrades': ['websocket'], 'pingTimeout': 20000, 'pingInterval': 25000}
  10.0.0.218 - - [31/Jul/2024 11:54:54] "GET /socket.io/?EIO=4&transport=polling&t=P49W5nO HTTP/1.1" 200 -
  RMoYImBp0_FvWs_lAAAA: Received packet MESSAGE data 0/test,
  RMoYImBp0_FvWs_lAAAA: Received request to upgrade to websocket
  emitting event "new_message" to all [/]
  RMoYImBp0_FvWs_lAAAA: Sending packet MESSAGE data 0/test,{"sid":"Fzb-QYWF3SFFBr4EAAAB"}
  10.0.0.218 - - [31/Jul/2024 11:54:54] "POST /socket.io/?EIO=4&transport=polling&t=P49W64r&sid=RMoYImBp0_FvWs_lAAAA HTTP/1.1" 200 -
  10.0.0.218 - - [31/Jul/2024 11:54:54] "GET /socket.io/?EIO=4&transport=polling&t=P49W64s&sid=RMoYImBp0_FvWs_lAAAA HTTP/1.1" 200 -
  RMoYImBp0_FvWs_lAAAA: Upgrade to websocket successful
  Client connected
  Starting Thread
  emitting event "new_message" to all [/]
  emitting event "new_message" to all [/]
  10.0.0.144 - - [31/Jul/2024 11:54:59] code 400, message Bad request syntax ('{"ts":1722441301,"dio":{"dio1":{"in":true},"dio2":{"in":true},"dio3":{"label":"Link","in":true},"dio4":{"in":true}},"ain":{"ain1":{"voltage":12},"ain2":{"voltage":2},"ain3":{"voltage":10},"ain4":{"voltage":0}}}')
  10.0.0.144 - - [31/Jul/2024 11:54:59] "{"ts":1722441301,"dio":{"dio1":{"in":true},"dio2":{"in":true},"dio3":{"label":"Link","in":true},"dio4":{"in":true}},"ain":{"ain1":{"voltage":12},"ain2":{"voltage":2},"ain3":{"voltage":10},"ain4":{"voltage":0}}}" HTTPStatus.BAD_REQUEST -
  emitting event "new_message" to all [/]
  emitting event "new_message" to all [/]
  emitting event "new_message" to all [/]
  10.0.0.218 - - [31/Jul/2024 11:55:05] "GET /socket.io/?EIO=4&transport=websocket&sid=RMoYImBp0_FvWs_lAAAA HTTP/1.1" 200 -
  10.0.0.218 - - [31/Jul/2024 11:55:05] "GET / HTTP/1.1" 200 -
  Client disconnected
  10.0.0.218 - - [31/Jul/2024 11:55:05] "GET /static/application.js HTTP/1.1" 304 -

HTML页面代码

<!DOCTYPE html>
<html>
<head>
    <script src="//cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
    <script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/socket.io/4.4.1/socket.io.min.js"></script>
    <script src="static/application.js"></script>

    <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
    <meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<div class="container">
  <div class="jumbotron">
    <h2>异步Flask通信</h2>
    <p>来自现场设备的WebSocket消息。</p>
  </div>
</div>

</div>

<div class="container" id="content">
    <div class="row">
        <p>异步页面更新将显示在这里:</p>
        <h3>消息:</h3>
        <div id="log">
        </div> <!-- /#log -->
    </div>
</div>


</body>
</html>

问题修复与优化方案

1. 400错误修复

控制台的400错误是因为你同时启动了两个服务抢占8089端口:

  • Flask-SocketIO在8089端口提供基于HTTP/WebSocket协议的SocketIO服务
  • 自定义的start_socket_server也绑定8089端口,监听原始TCP连接

外部设备直接发送JSON字符串到8089端口,被Flask的HTTP服务器接收(而非你的自定义TCP服务),因不符合HTTP请求格式被判定为无效请求,返回400错误。

修复方案二选一:

  • 给自定义TCP服务更换端口(比如8090),让外部设备连接该端口;
  • 移除自定义TCP服务,直接用Flask-SocketIO接收设备消息(推荐,简化架构)。

推荐方案的代码修改:

@socketio.on('device_message', namespace='/test')
def handle_device_message(message):
    # 验证发送方IP
    from flask import request
    client_ip = request.remote_addr
    if client_ip == '10.0.0.144':
        messages.append(message)
        socketio.emit('new_message', {'message': message}, namespace='/test')

外部设备需用SocketIO客户端发送device_message事件,而非直接发送JSON字符串。

2. 页面无更新修复

当前代码存在两个核心问题:

  • 后端socketio.emit未指定命名空间,前端连接默认命名空间,但后端connect事件绑定在/test命名空间,两者不匹配;
  • 前端JS尝试访问msg.number,但后端发送的是{'message': message},字段名不匹配。

修复步骤:

  1. 前端连接指定命名空间:
var socket = io.connect('/test');
  1. 前端修正消息处理逻辑:
socket.on('new_message', function(msg) {
    console.log("收到消息:" + msg.message);
    if (messages_received.length >= 10){
        messages_received.shift()
    }
    messages_received.push(msg.message);
    messages_string = '';
    for (var i = 0; i < messages_received.length; i++){
            messages_string += '<p>' + messages_received[i].toString() + '</p>';
    }
    $('#log').html(messages_string);
});
  1. 后端发送消息时指定命名空间:
socketio.emit('new_message', {'message': message}, namespace='/test')

最优实现方案

  • 移除自定义TCP服务器,完全基于Flask-SocketIO实现设备消息接收和页面推送,减少端口冲突与架构复杂度;
  • 保留设备IP验证逻辑,确保仅处理来自10.0.0.144的消息;
  • 使用SocketIO命名空间隔离设备连接与前端页面连接;
  • 给messages列表添加线程锁(threading.Lock),避免多线程访问时的并发冲突。

内容的提问来源于stack exchange,提问作者killedbydeath

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 09:14:52