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

Flask-SocketIO join_room功能异常:加入房间后在线数始终为0

解决Flask-SocketIO join_room后房间Socket数量显示为0的问题

问题场景

用Flask做后端、React做前端开发在线游戏大厅时,调用Flask-SocketIO的join_room方法后,打印房间内Socket数量始终显示为0,无法正确判断房间人数。

前端app.js连接代码

class SocketService {
    socket;

    connect(url) {
        return new Promise((rs, rj) => {
          this.socket = io(url);

          if (!this.socket) return rj();

          this.socket.on("connect", () => {
            rs(this.socket);
          });

          this.socket.on("connect_error", (err) => {
            console.log("Connection error: ", err);
            rj(err);
          });
        });
    }
}

const connectSocket = async () => {
    await socketService
      .connect("http://localhost:5001")
      .catch((err) => {
        console.log("Error: ", err);
      });
  };

  useEffect(() => {
    connectSocket();
  }, []);

后端app.py代码

from flask import Flask, send_from_directory, json
from flask_restful import Api
from flask_cors import CORS
from flask_socketio import SocketIO, rooms, join_room, send

app = Flask(__name__, static_url_path='', static_folder='client/build')
CORS(app, resources={r"/*": {"origins": "*"}})
api = Api(app)

SOCKETIO = SocketIO(app, cors_allowed_origins="*", json=json, manage_session=True, logger=True, engineio_logger=True)

@SOCKETIO.on('join_game')
def joinGame(message):
    connectedSockets = rooms(message['roomId'])
    print('Received join game request to room:', message['roomId'])
    print('Number of sockets in this room:', len(connectedSockets))

    if len(connectedSockets) >= 2:
        SOCKETIO.emit("room_join_error", {
            'error': "Room is full! Please choose another room to play."
        })
    else:
        roomId = message['roomId']
        join_room(roomId)
        print("A user joined this room: ", roomId)
        print("Number of users in room after joining: ", len(rooms(roomId)))
        send("A new user has joined this room.", to=roomId)
        SOCKETIO.emit("room_joined")

    if (len(connectedSockets) == 2):
        SOCKETIO.emit("start_game", {
            'start': True,
            'symbol': "X"
        })
        SOCKETIO.to(message['roomId']).emit("start_game", {
            'start': False,
            'symbol': "O"
        })

if __name__ == "__main__":
    SOCKETIO.run(
        app=app,
        port=5001,
        debug=True
    )

问题根源

  1. rooms()函数用法错误:原代码中rooms(message['roomId'])无法正确获取房间内的Socket列表。在Flask-SocketIO的事件处理函数中,rooms()不传参数时返回当前客户端已加入的所有房间;传入参数并非用于获取房间内客户端,而是判断客户端是否在指定房间。
  2. 房间名前缀问题:Flask-SocketIO默认会给房间名添加/前缀,直接用传入的roomId查询会找不到对应的房间。

修复方案

1. 修正房间内Socket数量的获取逻辑

替换原代码中获取房间连接数的部分,改用服务器管理器查询房间:

# 获取房间内客户端数量,需添加默认的/前缀
room_key = f"/{message['roomId']}"
connected_sockets = SOCKETIO.server.manager.rooms.get(room_key, [])
print('Number of sockets in this room:', len(connected_sockets))

2. 调整完整的事件处理逻辑

from flask import request  # 需添加此导入

@SOCKETIO.on('join_game')
def joinGame(message):
    roomId = message['roomId']
    room_key = f"/{roomId}"
    connected_sockets = SOCKETIO.server.manager.rooms.get(room_key, [])
    print('Received join game request to room:', roomId)
    print('Number of sockets in this room:', len(connected_sockets))

    if len(connected_sockets) >= 2:
        # 只给当前请求的客户端发送错误信息
        SOCKETIO.emit("room_join_error", {
            'error': "Room is full! Please choose another room to play."
        }, to=request.sid)
    else:
        join_room(roomId)
        # 重新获取更新后的房间连接数
        updated_connected_sockets = SOCKETIO.server.manager.rooms.get(room_key, [])
        print("A user joined this room: ", roomId)
        print("Number of users in room after joining: ", len(updated_connected_sockets))
        send("A new user has joined this room.", to=roomId)
        # 只给当前客户端发送加入成功通知
        SOCKETIO.emit("room_joined", to=request.sid)

    # 当房间人数变为2时触发游戏开始
    if len(updated_connected_sockets) == 2:
        # 给房间内另一个客户端发送X角色
        SOCKETIO.to(roomId).emit("start_game", {
            'start': True,
            'symbol': "X"
        }, skip_sid=request.sid)
        # 给当前客户端发送O角色
        SOCKETIO.emit("start_game", {
            'start': False,
            'symbol': "O"
        }, to=request.sid)

3. 前端补充事件发送与监听

确保连接成功后正确发送join_game事件并监听后端响应:

const connectSocket = async () => {
    try {
        const socket = await socketService.connect("http://localhost:5001");
        // 替换为实际的房间ID
        socket.emit('join_game', { roomId: 'your-room-id' });
        
        socket.on('room_joined', () => {
            console.log('成功加入房间');
        });
        socket.on('room_join_error', (err) => {
            console.error('加入房间失败:', err.error);
        });
        socket.on('start_game', (data) => {
            console.log('游戏开始,你的角色:', data.symbol);
        });
    } catch (err) {
        console.log("连接出错: ", err);
    }
};

额外注意事项

  • 版本兼容:确保Flask-SocketIO和前端socket.io-client版本匹配(如Flask-SocketIO 5.x对应socket.io-client 4.x)。
  • 精准推送:使用request.sid定位当前客户端,避免事件广播给所有用户。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 17:35:14