Matthew Walker
Matthew Walker

Reputation: 193

Socket.io - Cannot send messages to rooms

socket.on('join room', function (data) {
    var room = data.room;
    var msg = "<span style='color:darkgreen;'>" + data.user + " has joined chat.</span>";

    socket.join(room, function () {
        console.log(socket.id + " now in rooms ", socket.rooms);
    });

    io.in(room).emit('system message', msg);
});

If I change io.in(room) to socket.broadcast the system message goes through. For whatever reason though, it will not send messages to specific rooms, it just does nothing, no errors either. Any ideas what's going wrong here?

Upvotes: 1

Views: 179

Answers (2)

jfriend00
jfriend00

Reputation: 707148

socket.join() is asynchronous. It has not completed yet when you're trying to send to the room, so no message goes to that socket because it isn't yet in the room. So, change this:

socket.join(room, function () {
    console.log(socket.id + " now in rooms ", socket.rooms);
});

io.in(room).emit('system message', msg);

to this:

socket.join(room, function () {
    console.log(socket.id + " now in rooms ", socket.rooms);
    io.in(room).emit('system message', msg);
});

So, in this new version you're not sending to the room until AFTER the .join() has completed rather than before.

You can see a similar example right in the socket.io doc here.

Upvotes: 1

Gabriel Balsa Cant&#250;
Gabriel Balsa Cant&#250;

Reputation: 2012

It seems the code above is on back-end, correct?

Try the following, adapt it with your needs if necessary:

io.on('connection', function (socket) {
    // You should expect ONLY one user connected, 
    // > otherwise, you're creating multiple sessions :)
    console.log('a user connected');
    socket.on('join room', function (data) {
        var room = data.room;
        console.log('Room: ', room);
        var msg = "<span style='color:darkgreen;'>" + data.user + " has joined chat.</span>";

        socket.join(room); // No callback needed
        console.log(socket.id + " now in rooms ", socket.rooms);
        io.to(room).emit('system message', msg);
        // Change it to io.to(room)

    });
});

I noticed you're asking to join on front-end using:

$(".channelChange").click( function(evt) {
    var socket = io();
    $("#messages").html("");
    evt.stopPropagation();
    if($.inArray(evt.currentTarget, $(this).children())){
        console.log("user moved to " + evt.currentTarget.id);
    }
    setCookie("currentRoom", evt.currentTarget.id, 1);
    $(".roomName").html("<span class='3Dtext'>" + evt.currentTarget.id + "</span>");
    $("#enter-sound").get(0).play();
    getMessages(getCookie("currentRoom"));
    // Here you ask to join room
    socket.emit('join room', { user: getCookie("username"), room: getCookie("currentRoom") });
})

And receiving the system message on:

   $(function(){
        var socket = io();
        socket.on('system message', function(msg) {
            alert("test");
            $('#messages').append($('<div class="systemMessage">').html(msg));
            var height = $("#messages")[0].scrollHeight;
            $.mobile.silentScroll(height);
    });

The main problem as you'll see, is that you're calling io() everytime, this created different sessions, 1. Session one joins a room 2. A different session will await for system message

Hence, you need a single io() session, a quick work arround is to make a self invoking function:

const setIo = (function (){
        const _io = io();
        return () => _io;
    })()

And then Replace all io() declarations to: var socket = setIo();

such as:

$(".channelChange").click( function(evt) {
    var socket = setIo();
    $("#messages").html("");
    evt.stopPropagation();

This will create a single io() session instead of different ones, and re-use it everytime you call setIo()

You'll eventually see the alert pop up:

enter image description here

Upvotes: 2

Related Questions