为什么即使在客户离开房间广告加入另一个房间后,消息也会发送到所有房间?烧瓶插座
Why message is sent to all rooms even after the client leave the room ad join another room? flask socketio
当客户端离开当前房间加入另一个房间时,他的消息仍然发送到旧房间,如何让他的消息只在当前房间显示?
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\n{data}\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);
}
});
在您的 .js 文件中,您有:
let active_room = "General";
joinRoom(active_room);
就在文件的顶部。您的用户每次都会被踢回 "General"。尝试在您的服务器端放置一些打印语句,在您的客户端放置 console.log 语句,以仔细检查您所在的房间。
您似乎正在使用 Sandeep Sudhakaran's YouTube tutorial 创建聊天应用程序(我从 printSysMsg()
函数中猜到了)。
你的问题
我查看了您的代码,发现您在以下套接字事件 ("message"
) 上使用了 broadcast=True
:
@socketio.on('message')
def message(data):
# print(f"\n\n{data}\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上说:
When a message is sent with the broadcast option enabled, all clients
connected to the namespace receive it, including the sender.
我在 Socket.IO documentation 上阅读了命名空间和空间的概念:
Within each namespace, you can also define arbitrary channels that
sockets can join and leave.
我认为 Flask SocketIO 在概念上与 Socket.IO 的工作原理类似。因此,由于您没有为此消息事件指定特定的命名空间,设置 broadcast=True
意味着 "a default global namespace with the name '/' is used," 并且您的消息将发送给此命名空间中的每个人。
我可能是错的,因为我 3 天前才开始了解这个,但这意味着您的广播包括您在命名空间中创建的所有房间 '/ '.因此,join_room
和 leave_room
将无济于事,因为所有房间仍在同一命名空间下。他们都会收到消息。
当客户端离开当前房间加入另一个房间时,他的消息仍然发送到旧房间,如何让他的消息只在当前房间显示? 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\n{data}\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);
}
});
在您的 .js 文件中,您有:
let active_room = "General";
joinRoom(active_room);
就在文件的顶部。您的用户每次都会被踢回 "General"。尝试在您的服务器端放置一些打印语句,在您的客户端放置 console.log 语句,以仔细检查您所在的房间。
您似乎正在使用 Sandeep Sudhakaran's YouTube tutorial 创建聊天应用程序(我从 printSysMsg()
函数中猜到了)。
你的问题
我查看了您的代码,发现您在以下套接字事件 ("message"
) 上使用了 broadcast=True
:
@socketio.on('message')
def message(data):
# print(f"\n\n{data}\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上说:
When a message is sent with the broadcast option enabled, all clients connected to the namespace receive it, including the sender.
我在 Socket.IO documentation 上阅读了命名空间和空间的概念:
Within each namespace, you can also define arbitrary channels that sockets can join and leave.
我认为 Flask SocketIO 在概念上与 Socket.IO 的工作原理类似。因此,由于您没有为此消息事件指定特定的命名空间,设置 broadcast=True
意味着 "a default global namespace with the name '/' is used," 并且您的消息将发送给此命名空间中的每个人。
我可能是错的,因为我 3 天前才开始了解这个,但这意味着您的广播包括您在命名空间中创建的所有房间 '/ '.因此,join_room
和 leave_room
将无济于事,因为所有房间仍在同一命名空间下。他们都会收到消息。