为啥即使在客户离开房间并加入另一个房间后,消息也会发送到所有房间?烧瓶插座

Posted

技术标签:

【中文标题】为啥即使在客户离开房间并加入另一个房间后,消息也会发送到所有房间?烧瓶插座【英文标题】:Why message is sent to all rooms even after the client leave the room ad join another room? flask socketio为什么即使在客户离开房间并加入另一个房间后,消息也会发送到所有房间?烧瓶插座 【发布时间】:2019-11-14 09:24:15 【问题描述】:

当客户离开当前房间并加入另一个房间时,他的消息仍然发送到旧房间,我怎样才能让他的消息只出现在当前房间? leave_room 和 join_room 运行良好,因为它会在用户加入房间时通知用户,并通知其他房间的用户他离开了房间,那么为什么将消息发送到所有房间?

服务器端代码:

import os

from flask import Flask, session, render_template, redirect, url_for, escape, request, flash
from flask_session import Session
from flask_socketio import SocketIO, emit, send, join_room, leave_room
from time import localtime, strftime




app = Flask(__name__)
app.config["SECRET_KEY"] = os.getenv("SECRET_KEY")
socketio = SocketIO(app)

ROOMS = ["General", "Testing", "One More"]

@app.route("/", methods=["GET", "POST"])
def index():
    return render_template("index.html", rooms=ROOMS)



@socketio.on('message')
def message(data):

    # print(f"\n\ndata\n\n")
    send('msg': data['msg'], 'username': data['username'], 'time_stamp': strftime('%b-%d %I:%M%p', localtime()), 'room': data['room'], broadcast=True)


@socketio.on('join')
def on_join(data):
    username = data['username']
    room = data['room']
    join_room(room)
    send('msg': username + " has joined the " + room + " room.", room=data['room'])


@socketio.on('leave')
def on_leave(data):
    username = data['username']
    room = data['room']
    leave_room(room)
    send('msg': username + " has left the " + room + " room.", room=data['room'])



if __name__ == '__main__':
    socketio.run(app, debug=True)

客户端

document.addEventListener('DOMContentLoaded', () => 
    var socket = io.connect(location.protocol + '//' + document.domain + ':' + location.port);
    var username = localStorage.getItem("username");
    let active_room = "General";
    joinRoom(active_room);

    if (!username) 
        username = prompt("Please enter username");
        localStorage.setItem("username", username);
    
    if (username) 
        document.querySelector('#name-of-client').innerHTML = username;
    

    // send message to the server
    document.querySelector('#send-btn').onclick = () => 
        socket.send(
            'msg': document.querySelector('#input-msg').value,
            'username': username,
            'room': active_room
        );
        document.querySelector('#input-msg').value = '';
    ;

    socket.on('message', data => 

        const p = document.createElement('p');
        const span_username = document.createElement('span');
        const span_timestamp = document.createElement('span');
        const br = document.createElement('br');

        if (data.username) 
            span_username.innerHTML = data.username;
            span_timestamp.innerHTML = data.time_stamp;
            p.innerHTML = span_username.outerHTML + br.outerHTML + data.msg + br.outerHTML + span_timestamp.outerHTML + br.outerHTML;
            document.querySelector('#display-msg').append(p);
         else 
            printSysMsg(data.msg)
        


    );


    // room selection
    document.querySelectorAll('.select-room').forEach(p => 
        p.onclick = () => 
            let newRoom = p.innerHTML;
            if (newRoom === active_room) 
                msg = `You are alread in the $active_room room.`;
                printSysMsg(msg);
             else 
                leaveRoom(active_room);
                joinRoom(newRoom);
                active_room = newRoom;
            

        ;
    );

    // Leave Rooms
    function leaveRoom(room) 
        socket.emit('leave', 
            'username': username,
            'room': room
        );
    


    // Join Room
    function joinRoom(room) 

        socket.emit('join', 
            'username': username,
            'room': room
        );

        // Clear Messages
        document.querySelector('#display-msg').innerHTML = "";
    


    function printSysMsg(msg) 
        const p = document.createElement('p');
        p.innerHTML = msg;
        document.querySelector('#display-msg').append(p);
    

);

【问题讨论】:

【参考方案1】:

您似乎正在使用Sandeep Sudhakaran's YouTube tutorial 创建一个聊天应用程序(我是从printSysMsg() 函数中猜到的)。

你的问题

我查看了您的代码,发现您在以下套接字事件 ("message") 上使用了broadcast=True

@socketio.on('message')
def message(data):

    # print(f"\n\ndata\n\n")
    send('msg': data['msg'], 'username': data['username'], 'time_stamp': strftime('%b-%d %I:%M%p', localtime()), 'room': data['room'], broadcast=True)

解决方案

尝试将其限制在房间内,而不是广播您的消息事件:

send('msg': data['msg'], 'username': data['username'], 'time_stamp': strftime('%b-%d %I:%M%p', localtime()), 'room': data['room'], room=data["room"])

推理

记得在官方Flask-SocketIO documentation上说:

在启用广播选项的情况下发送消息时,所有客户端 连接到命名空间接收它,包括发送者。

我在Socket.IO documentation阅读了命名空间和房间的概念:

在每个命名空间中,您还可以定义任意通道 套接字可以加入和离开。

我假设 Flask SocketIO 在概念上与 Socket.IO 类似。因此,由于您没有为此消息事件指定特定的命名空间,因此设置 broadcast=True 意味着“使用名称为 '/' 的默认全局命名空间”,并且您的消息将发送给此命名空间中的每个人。

我可能是错的,因为我 3 天前才开始了解这个,但这意味着您的广播包括您在命名空间“/”中创建的所有房间。因此,join_roomleave_room 将无济于事,因为所有房间仍位于同一命名空间下。他们都会收到消息。

【讨论】:

【参考方案2】:

在您的 .js 文件中,您有:

let active_room = "General";
joinRoom(active_room);

就在文件的顶部。您的用户每次都会被踢回“常规”。尝试在服务器端放置一些 print 语句,在客户端放置一些 console.log 语句来仔细检查你所在的房间。

【讨论】:

以上是关于为啥即使在客户离开房间并加入另一个房间后,消息也会发送到所有房间?烧瓶插座的主要内容,如果未能解决你的问题,请参考以下文章

Socket.io:如何正确加入和离开房间

光子。在 Android 上重启 WI-FI 后 OnRoomListUpdate 不起作用

如何使用 Flask 和 Socket.io 加入和离开房间的简明示例?

多次加入同一个房间和一个房间的客户

当有人在 smackx 中加入或离开 MultiUserChat 时,如何找出房间的入住者?

套接字 io 不向房间发送消息