SocketCluster / socketcluster-client

JavaScript client for SocketCluster
MIT License
292 stars 91 forks source link

watch() will be re-occur when socket reconnected #24

Closed smilence86 closed 8 years ago

smilence86 commented 8 years ago

hello, I try to use socketcluster, and I found watch() would re-triggered when socket reconnected, How to solve it? thanks ~

my code:

    var connectCount = 1;
    var options = {
        port: 8123
    };
    var socket = socketCluster.connect(options);
    socket.on('connect', function(socket){
        console.log(arguments);
        console.log('CONNECTED');
    });
    socket.on('error', function(err){
        console.error('Socket error - ' + err);
        reconnect();
    });
    socket.on('disconnect', function(err){
        console.error('Socket disconnect - ' + err);
        reconnect();
    });
    function reconnect(){
        if(connectCount <= 100 && socket.state != 'open'){
            setTimeout(function(){
                console.log('reconnect:' + connectCount);
                socket.connect();
                ++connectCount;
            }, 5000);
        }
    }

    socket.on('event', function(data){
        $('#result').append(JSON.stringify(data) + '<br/>');
    });

    var channel = socket.subscribe('channel');
    channel.on('subscribeFail', function(err){
        console.log('Failed to subscribe the channel due to error: ' + err);
    });
    channel.watch(function(data){
        $('#result').html(data + '<br/>');
    });
jondubois commented 8 years ago

You don't need to write the reconnect behaviour yourself - SC does that automatically by default (though you can turn it off by setting the autoReconnect option here to false: http://socketcluster.io/#!/docs/api-socketcluster-client - But I wouldn't recommend it :p).

Note that in SC, when the socket becomes disconnect, all state is maintained so when you connect again, it will restore everything exactly as it was. In this case the watcher function is never removed, so it will stay bound between disconnect/reconnect.

If you want to get rid of a watcher you need to call channel.unwatch(handlerFn). See http://socketcluster.io/#!/docs/api-scchannel-client. Or you can call it on the socket too: socket.unwatch('channelName', handlerFn).

smilence86 commented 8 years ago

@jondubois thanks~

smilence86 commented 8 years ago

@jondubois Sorry I don't solve it really, you said: In this case the watcher function is never removed, so it will stay bound between disconnect/reconnect.

I set autoReconnect to true with your recommend, The watch event triggered again by page refreshed, I try to unwatching when socket becomes disconnected both client & server sides. It seems not work.

jondubois commented 8 years ago

@smilence86 It works for me. Maybe try using the latest version of SocketCluster client and server and check that you're passing the correct arguments to functions. http://socketcluster.io/#!/docs/api-scsocket-client

jondubois commented 8 years ago

When you call channel.unwatch(myFunction), make sure that myFunction is exactly the same reference as what you provided to channel.watch(myFunction).

smilence86 commented 8 years ago

yes, I confirm that SC is newest, this is the code below:

$(document).ready(function(){

    var channel = {};
    var options = {
        port: 8123
    };
    var socket = socketCluster.connect(options);
    socket.on('connect', function(status){
        console.log(status);
        console.log('CONNECTED');
        channel = bindChannel(socket);
    });
    socket.on('error', function(err){
        console.error('Socket error - ' + err);
    });
    socket.on('disconnect', function(err){
        console.error('Socket disconnect - ' + err);
        channel.unwatch(watcher);
        // socket.unwatch('channel_demo', function(){
        //     console.log(arguments);
        // });
    });

    $('#btn').click(function(){
        socket.emit('event', {message: 'This is an object with a message property'});
    });
    socket.on('event', function(data){
        $('#result').append(JSON.stringify(data) + '<br/>');
    });

    var watcher = function(data){
        $('#result').html(data + '<br/>');
    }
    function bindChannel(socket){
        var channel = socket.subscribe('channel_demo');
        channel.on('subscribeFail', function(err){
            console.log('Failed to subscribe the channel due to error: ' + err);
        });
        channel.watch(watcher);
        //console.log(channel.watchers());
        return channel;
    }
});
jondubois commented 8 years ago

@smilence86 This is expected since you are calling channel.watch(watcher) every time the 'connect' event triggers. Note that the 'connect' event will trigger on reconnect too.

So what you're doing now is you're unwatching it on disconnect, but then as soon as it reconnects, you are setting up the watcher again.

smilence86 commented 8 years ago

@jondubois I leave my code, please help fix it. There are 3 workers, channel.watch will trigger twice when page refresh 4 times and more. But It looks well after I set only one worker, No matter page refreshed how many times.

client:

$(document).ready(function(){

    var channel = {};
    var options = {
        port: 8123
    };
    var socket = socketCluster.connect(options);
    socket.on('connect', function(status){
        console.log(status);
        console.log('CONNECTED');
        channel = bindChannel(socket);
    });
    socket.on('error', function(err){
        console.error('Socket error - ' + err);
    });
    socket.on('disconnect', function(err){
        console.error('Socket disconnect - ' + err);
    });

    var watcher = function(data){
        $('#result').html(data + '<br/>');
    }

    function bindChannel(socket){
        var channel = socket.subscribe('channel_demo');
        channel.on('subscribeFail', function(err){
            console.log('Failed to subscribe the channel due to error: ' + err);
        });
        channel.watch(watcher);
        return channel;
    }
});

server:

var socket = {};
scServer.on('connection', function (sock){
    log('socket_id: ' + sock.id);
    socket = sock;
    socket.on('disconnect', function () {
        console.error('User disconnected');
    });
});

setInterval(function(){
    if(socket && socket.exchange){
        socket.exchange.channel('channel_demo').publish('push data to channel:' + utils.random(1, 100), function(){
            console.log(arguments);
        });
    }
}, 1000);