Socket.IO / JavaScript 向房间广播消息时出现问题
Socket.IO / JavaScript problem when broadcasting message to room(s)
我正在使用 Socket.IO 开发一个聊天应用程序(服务器使用 flask-SocketIO)。用户可以创建新频道(房间)并在它们之间切换。在我下面的代码中,出于某种原因,每次我切换(返回)到一个房间(即使只有一个房间并且 "switching" 回到它),"broadcast message"-处理函数都会额外执行一次。 IE。如果我在 "channel_1" 上发送 "Hello",切换回另一个频道,然后返回 "channel_1",然后发送 "Hello again",它会被广播(在我的示例中为 console.log )两次。下次我切换回 "channel_1"、3 TIMES 等。我认为它一定与 JS 代码有关,也许是调用 connectSocket() 的方式,因为 flask-app 只发出 "broadcast message" 每次一次。为冗长的代码道歉 - 我尽我所能遗漏了不相关的部分。
document.addEventListener('DOMContentLoaded', () => {
// IF USER SWITCHES / SELECTS EXISTING CHANNEL
document.querySelector('#select_channel').onsubmit = () => {
var channel = document.querySelector('select').value;
const r2 = newXHR();
r2.open('POST', '/select_channel');
const data = new FormData();
data.append('channel', channel);
r2.onload = () => {
connectSocket(channel);
};
r2.send(data);
return false;
}
// IF USER CREATES NEW CHANNEL
document.querySelector('#new_channel').onsubmit = () => {
const new_channel_name = document.querySelector('#new_channel_name').value;
const r1 = newXHR();
r1.open('POST', '/new_channel');
const data = new FormData();
data.append('new_channel_name', new_channel_name);
r1.onload = () => {
const response = JSON.parse(r1.responseText);
if (response.channel_exists) {
alert("Channel already exists");
return false;
}
else {
const option = document.createElement('option');
option.innerHTML = new_channel_name;
document.querySelector('select').append(option);
connectSocket(new_channel_name);
document.getElementById('new_channel').reset();
}
};
r1.send(data);
return false;
};
});
function connectSocket(channel) {
var socket = io();
socket.on('connect', () => {
// if user previously connected to any channel, disconnect him
if (localStorage.getItem('channel') != null)
{
socket.emit('leave', {'room': localStorage.getItem('channel'), 'username': display_name});
}
socket.emit('join', {'room': channel, 'username': display_name});
localStorage.setItem('channel', channel);
const data = new FormData();
data.append('username', display_name);
data.append('room', channel);
document.querySelector('#current_channel').innerHTML = channel;
});
document.querySelector('#send_message').onsubmit = () => {
var message = document.querySelector('#message').value;
socket.emit('send', {'message': message, 'room': channel});
console.log(`SENDING ${message}`);
return false;
}
// PROBLEM: EVERY TIME CHANNEL CHANGED AND MSG SENT IN THAT CHANNEL -> 1 EXTRA COPY OF THAT MESSAGE IS BROADCAST - I>E> THE BELOW IS DONE +1 TIMES
socket.on('broadcast message', function handle_broadcast (data) {
console.log(data);
});
}
Python 个片段:
# [IMPORT & CONFIG STATEMENTS...]
socketio = SocketIO(app, logger=True, engineio_logger=True)
# Global variables
channels = []
messagetext = None
@app.route("/select_channel", methods=["GET", "POST"])
def select_channel():
if request.method == "POST":
channel = request.form.get("channel")
session["channel"] = channel
return jsonify({"success": True})
return render_template("chat.html", channels = channels)
@app.route("/new_channel", methods=["GET", "POST"])
def new_channel():
if request.method == "POST":
new_channel = request.form.get("new_channel_name")
if new_channel in channels:
return jsonify({"channel_exists": True})
else:
channels.append(new_channel)
session["channel"] = new_channel
return json.dumps(channels)
return render_template("chat.html", channels = channels)
@socketio.on('join')
def on_join(data):
username = data['username']
room = data['room']
join_room(room)
send(username + ' has entered the room.', room=room)
@socketio.on('leave')
def on_leave(data):
username = data['username']
room = data['room']
leave_room(room)
send(username + ' has left the room.', room=room)
@socketio.on("send")
def handle_send(data):
messagetext = data["message"]
room = data["room"]
emit("broadcast message", {"message": messagetext}, room=room)
if __name__ == '__main__':
socketio.run(app, debug=True)
我认为在 Flask-SocketIO
库中,当您加入一个房间时,如果您不输入 sid
,它会使用 flask.request.sid
。我不确定 Flask-SocketIO
对那个 属性 使用了什么,但我的猜测是当你加入一个房间时,设置了一个 sid
。当您离开房间时,可能 正在使用不同的 sid
,这意味着您的原始客户实际上并没有离开房间。因此,当他们再次加入时,会建立一个新连接(意味着第二个并发连接),这可以解释为什么您会多次收到广播消息。
我建议尝试创建自己的 sid
以传递到 join_room()
和 leave_room()
函数中,看看是否可以解决问题。您可以将它从客户端传递到您的服务器,并且只是为了测试它可以像 session1
.
这样简单的东西
希望对您有所帮助。
我正在使用 Socket.IO 开发一个聊天应用程序(服务器使用 flask-SocketIO)。用户可以创建新频道(房间)并在它们之间切换。在我下面的代码中,出于某种原因,每次我切换(返回)到一个房间(即使只有一个房间并且 "switching" 回到它),"broadcast message"-处理函数都会额外执行一次。 IE。如果我在 "channel_1" 上发送 "Hello",切换回另一个频道,然后返回 "channel_1",然后发送 "Hello again",它会被广播(在我的示例中为 console.log )两次。下次我切换回 "channel_1"、3 TIMES 等。我认为它一定与 JS 代码有关,也许是调用 connectSocket() 的方式,因为 flask-app 只发出 "broadcast message" 每次一次。为冗长的代码道歉 - 我尽我所能遗漏了不相关的部分。
document.addEventListener('DOMContentLoaded', () => {
// IF USER SWITCHES / SELECTS EXISTING CHANNEL
document.querySelector('#select_channel').onsubmit = () => {
var channel = document.querySelector('select').value;
const r2 = newXHR();
r2.open('POST', '/select_channel');
const data = new FormData();
data.append('channel', channel);
r2.onload = () => {
connectSocket(channel);
};
r2.send(data);
return false;
}
// IF USER CREATES NEW CHANNEL
document.querySelector('#new_channel').onsubmit = () => {
const new_channel_name = document.querySelector('#new_channel_name').value;
const r1 = newXHR();
r1.open('POST', '/new_channel');
const data = new FormData();
data.append('new_channel_name', new_channel_name);
r1.onload = () => {
const response = JSON.parse(r1.responseText);
if (response.channel_exists) {
alert("Channel already exists");
return false;
}
else {
const option = document.createElement('option');
option.innerHTML = new_channel_name;
document.querySelector('select').append(option);
connectSocket(new_channel_name);
document.getElementById('new_channel').reset();
}
};
r1.send(data);
return false;
};
});
function connectSocket(channel) {
var socket = io();
socket.on('connect', () => {
// if user previously connected to any channel, disconnect him
if (localStorage.getItem('channel') != null)
{
socket.emit('leave', {'room': localStorage.getItem('channel'), 'username': display_name});
}
socket.emit('join', {'room': channel, 'username': display_name});
localStorage.setItem('channel', channel);
const data = new FormData();
data.append('username', display_name);
data.append('room', channel);
document.querySelector('#current_channel').innerHTML = channel;
});
document.querySelector('#send_message').onsubmit = () => {
var message = document.querySelector('#message').value;
socket.emit('send', {'message': message, 'room': channel});
console.log(`SENDING ${message}`);
return false;
}
// PROBLEM: EVERY TIME CHANNEL CHANGED AND MSG SENT IN THAT CHANNEL -> 1 EXTRA COPY OF THAT MESSAGE IS BROADCAST - I>E> THE BELOW IS DONE +1 TIMES
socket.on('broadcast message', function handle_broadcast (data) {
console.log(data);
});
}
Python 个片段:
# [IMPORT & CONFIG STATEMENTS...]
socketio = SocketIO(app, logger=True, engineio_logger=True)
# Global variables
channels = []
messagetext = None
@app.route("/select_channel", methods=["GET", "POST"])
def select_channel():
if request.method == "POST":
channel = request.form.get("channel")
session["channel"] = channel
return jsonify({"success": True})
return render_template("chat.html", channels = channels)
@app.route("/new_channel", methods=["GET", "POST"])
def new_channel():
if request.method == "POST":
new_channel = request.form.get("new_channel_name")
if new_channel in channels:
return jsonify({"channel_exists": True})
else:
channels.append(new_channel)
session["channel"] = new_channel
return json.dumps(channels)
return render_template("chat.html", channels = channels)
@socketio.on('join')
def on_join(data):
username = data['username']
room = data['room']
join_room(room)
send(username + ' has entered the room.', room=room)
@socketio.on('leave')
def on_leave(data):
username = data['username']
room = data['room']
leave_room(room)
send(username + ' has left the room.', room=room)
@socketio.on("send")
def handle_send(data):
messagetext = data["message"]
room = data["room"]
emit("broadcast message", {"message": messagetext}, room=room)
if __name__ == '__main__':
socketio.run(app, debug=True)
我认为在 Flask-SocketIO
库中,当您加入一个房间时,如果您不输入 sid
,它会使用 flask.request.sid
。我不确定 Flask-SocketIO
对那个 属性 使用了什么,但我的猜测是当你加入一个房间时,设置了一个 sid
。当您离开房间时,可能 正在使用不同的 sid
,这意味着您的原始客户实际上并没有离开房间。因此,当他们再次加入时,会建立一个新连接(意味着第二个并发连接),这可以解释为什么您会多次收到广播消息。
我建议尝试创建自己的 sid
以传递到 join_room()
和 leave_room()
函数中,看看是否可以解决问题。您可以将它从客户端传递到您的服务器,并且只是为了测试它可以像 session1
.
希望对您有所帮助。