Concise example of how to join and leave rooms using Flask and Socket.io?

10,035

Solution 1

Think of a room as an array of users that stays on the server. When you send your message in "send message", you set broadcast=True, so it sends it as a global message to all users, as long as they are connected. If you only want to send to users in specific rooms, you will need to specify which room you want to send the message to from the client, each time you send a message, like this:

// client.js

socket.emit('join', { 'channel': channel, ... });
socket.emit('send message', {'message': message, 'channel': channel});

// server.py

@socketio.on("send message")
def message(data):
    room = data['channel']
    emit('broadcast message', data['message'], room=room)

Solution 2

I believe the first answer is correct. Just as a note, avoid using 'innerhtml' where possible especially in this case. By setting the innerhtml, anything a user writes in a message will be treated as html at the other end. This includes script tags which means someone could remotely run javascript on someone else's machine by sending a malicious message.

I would suggest using innerText or textContent. This will treat the message as plain text not html. They are slightly different so it may be worth looking into which one you need.

I would have done this as a comment but my rep isn't high enough.

tl:dr Use textContent or innerText instead of innerhtml.

Share:
10,035
Admin
Author by

Admin

Updated on June 17, 2022

Comments

  • Admin
    Admin about 2 years

    I'm trying to use socket.io with a Flask server connecting to JS.. I'm struggling with basically everything, but my first step is to make it so that users can connect to different channels. My broadcast message function is working, but when I click on a different channel, the messages do not get sent to a different channel.. What am I doing wrong?

    JS:

    document.addEventListener('DOMContentLoaded', ()=>{
    
      // Send user back to login page if they didn't sign in
      const username = localStorage.getItem('username');
      if (username == null){
        window.location = "/";
      }
    
      // Switch button active class when clicked
      $('.list-group .list-group-item.list-group-item-action').click(function(e) {
        $('.list-group .list-group-item.list-group-item-action.active').removeClass('active');
        var $this = $(this);
        if (!$this.hasClass('active')) {
            $this.addClass('active');
        }
        e.preventDefault();
      });
    
      // Connect to socket.io
      var socket = io.connect(location.protocol + '//' + document.domain + ':' + location.port);
    
      socket.on('connect', () => {
    
        // Automatically connect to general channel
        socket.emit('join',{"channel": "general", "username":username});
    
        // When a channel is clicked, connect to that channel
        document.querySelectorAll('.list-group-item').forEach(function(channel){
          channel.onclick = () =>{
            socket.emit('join',{"channel":channel.innerHTML, "username":username});
            return false;
          }
        });
    
        // When a message is sent, call 'send message' function from server
        document.querySelector('#send-message').onsubmit = () => {
          const message = document.querySelector('#m').value
          socket.emit('send message', {'message': message});
    
          // Clear message form
          document.querySelector('#m').value = "";
    
          return false;
        };
      });
    
    
    
      // Callback from server for sending messages
      socket.on('broadcast message', data =>{
        console.log(data);
    
        // Append message to list of messages
        const li = document.createElement('li');
        li.innerHTML = `${data.message}`;
        document.querySelector('#messages').append(li);
    
      });
    });
    

    Python Flask:

    import os
    
    from flask import Flask, render_template, url_for
    from flask_socketio import SocketIO, emit, join_room, leave_room
    from collections import defaultdict
    
    app = Flask(__name__)
    app.config["SECRET_KEY"] = os.getenv("SECRET_KEY")
    socketio = SocketIO(app)
    
    messages = defaultdict(list)
    channels = ["Programming"]
    
    @app.route("/")
    def index():
        return render_template("login.html")
    
    @app.route("/chatroom/")
    def chatroom():
        return render_template("chatroom.html", channels=channels, messages=messages)
    
    @socketio.on("send message")
    def message(data):
        print(data)
        emit("broadcast message",  {"message": message}, broadcast=True)
    
    @socketio.on('join')
    def on_join(data):
        username = data['username']
        channel = data['channel']
        join_room(channel)
        #send(username + ' has entered the room.', channel=channel)
    
    if __name__ == '__main__':
        socketio.run(app)
    
  • Kakashi Hatake
    Kakashi Hatake about 3 years
    I am a beginner, Can you please tell me what the third parameter room=room in the emit does, and how do we use it on the client side. I am just clueless about the whole room thing and how does send and emit handle it.
  • Jacob Sussan
    Jacob Sussan almost 3 years
    @KakashiHatake room is an optional parameter that emit has. When included it will only emit the message to that specific room, and only clients that have joined that room will get said message. A room is an arbitrary channel that sockets can join and leave. It can be used to broadcast events to a subset of clients. Rooms are a server-only concept (i.e. the client does not have access to the list of rooms it has joined). Hope this helps.