Cito
Cito

Reputation: 1709

Socket.io emmit the messages twice (or more)

I have this code in my server.js file at node.js:

var app = require('http').createServer(handler), io = require('socket.io').listen(app);
var xml2js = require('xml2js'), parser = new xml2js.Parser(), fs = require('fs');

// creating the server ( localhost:8000 )
app.listen(8000);

/**
 * Esta función es la que envía el archivo js necesario para la comunicación
 * push con sockets
 * 
 * @param req
 * @param res
 */
function handler(req, res) {
    Request = require('url').parse(req.url, true);
    site = Request.query.site;
    entity = Request.query.entity;
    id = Request.query.id;

    fs.readFile(__dirname + '/client.js', 'utf8', function(err, data) {
        if (err) {
            console.log(err);
            res.writeHead(500);
            return res.end('Error loading client.js');
        }
        var dataString = data.toString();
        dataString = dataString.replace('confSite', site);
        dataString = dataString.replace('confEntity', entity);
        dataString = dataString.replace('confId', id);
        res.writeHead(200, {
            'Content-Type' : 'text/javascript;charset=UTF-8'
        });
        res.end(dataString);
    });
};

var listeners = {};
var parsers = {};
var listenersAndSockets = {};
var socketsOnListeners = {};
var watchers = {};
/**
 * Esta función es la que enviará la información que se actualice a los
 * clientes. Escuchará un archivo xml el cual envará al cliente una vez que este
 * haya cambiado
 */
io.sockets.on('connection', function(socket) {
    socket.on ('connect', function() {
        console.log('conectado');
    });
    socket.on('setup', function(config) {
        console.log('setup');
        var site = config.site;
        var entity = config.entity;
        var id = config.id;
        var listenerName = '' + site + entity + id + '';
        watchers[listenerName] = site + '/' + '/' + entity + '/' + id + '.xml';
        socketsOnListeners[socket.id] = listenerName;
        if (typeof socketsOnListeners[listenerName] == 'undefined') {
            socketsOnListeners[listenerName] = {};
        }
        socketsOnListeners[listenerName][socket.id] = socket.id;
        if (typeof listeners[listenerName] == 'undefined') {
            parsers[listenerName] = new xml2js.Parser();
            fs.stat(watchers[listenerName], function(err, stats) {
                if (err) {
                    fs.writeFile(watchers[listenerName], '');
                }
            });
            listeners[listenerName] = function(curr, prev) {
                fs.readFile(watchers[listenerName], function(err, data) {
                    parsers[listenerName].parseString(data);
                });
            };
            fs.watch(watchers[listenerName], listeners[listenerName]);
        }
        parsers[listenerName].addListener('end', function(result,a) {
            socket.volatile.emit('notification', result);
        });
    });

    socket.on('disconnect', function() {
        delete socketsOnListeners[socketsOnListeners[socket.id]][socket.id];
        if (socketsOnListeners[socketsOnListeners[socket.id]].lenght == 0) {
            fs.unwatch(watchers[socketsOnListeners[socket.id]]);
            delete watchers[socketsOnListeners[socket.id]];
        }
        delete socketsOnListeners[socket.id];
    });
});

And in my test.html I have this:

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Prueba de Push</title>
</head>
<body>

    <div id="div1">Este es un texto de prueba</div>

    <script src="http://10.0.0.113:8000/socket.io/socket.io.js"></script>
    <script src="http://code.jquery.com/jquery-1.7.1.min.js"></script>
    <script src="http://10.0.0.113:8000/?site=levelup&entity=noticia&id=1"></script>

    <script>
        function test(data) {
            console.log(data);
            jQuery('#' + data.id).html(data.content);
        }
    </script>


</body>
</html>

Client.js is this:

var socket = io.connect('http://10.0.0.113:8000');
var config = {
    site : 'confSite',
    entity : 'confEntity',
    id : 'confId'
};

socket.emit("setup", config);
socket.on('reconnect', function() {
    socket.emit("setup", config);
});
// on every message recived we print the new datas inside the #container div
socket.on('notification', function(data) {
    _efbn(data.callback, window, data.response, data);
});

/**
 * Función que ejecuta una función por nombre. Puede usar namespaces
 * (algo.algo.algo.funct)
 * 
 * @see http://stackoverflow.com/questions/359788/javascript-function-name-as-a-string/359910#359910
 */
function _efbn(functionName, context) {
    var args = Array.prototype.slice.call(arguments);
    args = [ args[2], args[3] ]; // Fix para IE.
    var namespaces = functionName.split(".");
    var func = namespaces.pop();
    for ( var i = 0; i < namespaces.length; i++) {
        context = context[namespaces[i]];
    }
    try {
        if (typeof context[func] == 'function') {
            return context[func].apply(this, args);
        }
    } catch (e) {
        console.log(e);
    }
    return null;
}

And in firefox, I'm receiving twice or more the notification when I modify the file on the server. Is there any way to prevent this? I have read something about groups at node.js... will that maybe help me?

Upvotes: 4

Views: 1930

Answers (1)

Vadim Baryshev
Vadim Baryshev

Reputation: 26179

You start watching file in setup event. At client side setup event may be emited several times (reconnect event). For each watch call new listener will be added to file. You need to check is watch listener already exists on file (for this socket) before setup it. Also you need unwatch file after socket will be closed. Otherwise you will get a memory leak.

UPDATE

Also you need to move

parser.addListener('end', function(result) {
    socket.volatile.emit('notification', result);
});

outside setup event.

Upvotes: 2

Related Questions