4

I'm writing a multi-room chat app with Node.js and Socket.IO and I'm wondering how to handle data that needs to persist for the duration of a session, when the transport is xhr-polling.

Currently, on the client I'm sending a "join" event on connection, which passes a long a few variables (such as username), which I store server-side using Socket.IO's get and set methods. They are then available until the client disconnects.

This works fine if the transport is Websockets, but if it falls back to xhr-polling, the join event is emitted on each connect, which happens every 5-10 seconds. (Similarly, "such and such has left/joined the room" is sent on each xhr-poll request, which is also undesirable.)

I'm not sure what the best way forward is. I can't disable xhr-polling as it's a needed fallback for flashless IE for one.

Here is the relevant client code:

socket.on("connect", function(){
    socket.emit("join", { username: username, room: room });
});

And on the server:

var io = require("socket.io").listen(8124)
, buffer = {}
, max_room_buffer = 15;

io.sockets.on("connection", function(socket) {
    socket.on("join", function(data){
        if(data.username && data.room) {
        socket.set("data", data);
        socket.join(data.room);
        if(!buffer[data.room]) buffer[data.room] = [];
        socket.broadcast.to(data.room).emit("message", "<em>" + data.username + " has joined room " + data.room + "</em>");
        socket.emit("message", "<em>You have joined room " + data.room + "</em>");
        socket.emit("message", buffer[data.room]);
    }
});

socket.on("message", function(message) {
    if(message) {
        socket.get("data", function(err, data){
            if(data) {
                var msg = "<span>" + data.username + ":</span> " + message;
                buffer[data.room].push(msg);
                if(buffer[data.room].length > max_room_buffer) buffer[data.room].shift();
                io.sockets.in(data.room).emit("message", msg);
            }
        });
    }
});

socket.on("disconnect", function () {
    socket.get("data", function(err, data){
        if(data) {
            socket.leave(data.room);
            socket.broadcast.to(data.room).emit("message", "<em>" + data.username + " has left room " + data.room + "</em>");
        }
    });
});

});

Thanks in advance.

Mottokrosh
  • 153
  • 1
  • 6
  • I think shouldn't the socket.io itself handle that instead of the developer handle it.. weird. – Rishav Rastogi Mar 02 '12 at 11:30
  • I guess something like chunked transfer encoding , would be supported not sure if "AJAX multipart streaming" , not sure if its the same thing – Rishav Rastogi Mar 02 '12 at 11:33
  • I thought that this must be an issue every chat app runs into with socket.io but I can't seem to find anything on it on Google. There's probably a best practice for dealing with this but I'm new to socket.io. – Mottokrosh Mar 02 '12 at 11:49

1 Answers1

2

Perhaps I'm missing something, but wouldn't this work.

Client:

var firstJoin = true;
socket.on("connect", function(){
    socket.emit("join", { username: username, room: room, firstJoin: firstJoin });
    firstJoin = false;
});

Server:

io.sockets.on("connection", function(socket) {
    socket.on("join", function(data){
        if(data.username && data.room) {
        socket.set("data", data);
        socket.join(data.room);
        if(!buffer[data.room]) buffer[data.room] = [];

        if (data.firstJoin) {
            socket.broadcast.to(data.room).emit("message", "<em>" + data.username + " has joined room " + data.room + "</em>");
            socket.emit("message", "<em>You have joined room " + data.room + "</em>");
            socket.emit("message", buffer[data.room]);
        }
    }
});

As for the fact that the connection event occurs on every new poll, that's an unavoidable consequence of XHR polling. Since each poll is a new HTTP request, there is no state from the previous request, so you need to set it up again.

If you want to reduce how frequently this happens, you could try increasing the polling duration from its default of 20. For example:

io.configure(function() {
    io.set('polling duration', 60);
});
Rohan Singh
  • 20,497
  • 1
  • 41
  • 48
  • That looks like it would take care of some of the problems. I'd have to live without a notice when a user really disconnects, unless there is a way to fire an event if a client hasn't pinged in a certain amount of time? – Mottokrosh Mar 02 '12 at 12:24
  • Yeah, that's not an easy problem to solve. In theory the disconnect event should fire after a while (I think the default `close timeout` is 25 seconds), but there can be a delay: http://stackoverflow.com/questions/6280569/socket-io-xhr-polling-delay-on-disconnection – Rohan Singh Mar 02 '12 at 12:30
  • Thanks Rohan, I'll check out that thread. I'd love to upvote your answer but as a new user I don't have enough reputation yet! – Mottokrosh Mar 02 '12 at 12:39