【问题标题】:Socket.IO / JavaScript problem when broadcasting message to room(s)向房间广播消息时出现 Socket.IO / JavaScript 问题
【发布时间】:2020-02-11 10:32:00
【问题描述】:

我正在开发一个带有 Socket.IO 的聊天应用程序(使用 flask-SocketIO 的服务器)。用户可以创建新频道(房间)并在它们之间切换。在我下面的代码中,出于某种原因,每次我切换(返回)到一个房间(即使有一个房间并“切换”回它),“广播消息”处理函数都会执行一次额外的时间。 IE。如果我在“channel_1”上发送“Hello”,切换回另一个频道,然后返回“channel_1”,然后再次发送“Hello”,它会被广播(在我的示例中为 console.log)TWICE。下次我切换回“channel_1”时,3 TIMES等。我认为它一定与JS代码有关,也许是调用connectSocket()的方式,因为flask-app只发出“广播消息”每次一次。为冗长的代码道歉 - 我尽可能地省略了不相关的部分。

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 sn-ps:

# [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)

【问题讨论】:

标签: javascript socket.io flask-socketio


【解决方案1】:

我认为在Flask-SocketIO 库中,当你加入一个房间时,如果你没有传入sid,它会使用flask.request.sid。我不确定Flask-SocketIO 对该属性使用了什么,但我猜当你加入一个房间时,会设置一个sid。当您离开房间时,可能正在使用不同的sid,这意味着您的原始客户实际上并没有离开房间。因此,当他们再次加入时,会建立一个新连接(即第二个并发连接),这可以解释为什么您会多次收到广播消息。

我建议您尝试创建自己的 sid 以传递给 join_room()leave_room() 函数,看看是否能解决问题。您可以将它从客户端传递到您的服务器,只是为了测试它可能是简单的,例如 session1

我希望这会有所帮助。

【讨论】:

  • 太棒了 - SID 是问题所在。创建我自己的似乎很棘手,但最终重新改组了代码,以便套接字“连接”处理程序只被调用一次(这似乎是在设置新的 SID 时),成功了。谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-05-04
  • 2018-12-23
  • 2023-03-27
  • 2023-03-16
  • 2020-02-05
  • 2018-08-08
  • 2017-11-06
相关资源
最近更新 更多