enmasseio / timesync

Time synchronization between peers

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

socket is null in `send` function

crazyyi opened this issue · comments

commented

I have used this module in a realtime web game. The websocket module I use is uws. But sometimes I get the error saying it couldn't perform send function on null and located to this line of my code. But this doesn't happen all the time. It only happens occasionally so it is hard to reproduce. What could possibly be the reason?

      ts.send = function(socket, data) {
      	socket.send(JSON.stringify(data));
  };

The above code is similar to socket.io example in your code but I am actually using a native browser websocket implementation.

I wanted to give a uws/ws implementation example to your code but I don't have much free time at the moment.

Hm, without being able to reproduce the issue it's impossible to track it down. How does your receiving side look like? The socket.io example looks like:

   socket1.on('timesync', function (data) {
      //console.log('receive', data);
      ts.receive(null, data);
    });

And can you post a full stacktrace of such an error?

I've been able to replicate this issue. I now know why generally why it's happening, and I have a way to get around the issue, but I'm not sure how to fix it in this library.

Here's the code which generates my timesync client:

export function createTimesyncClient({socket, onOffsetChange}){
    const ts = timesync.create({server: socket});

    ts.send = (innerSocket, data, timeout) =>
        new Promise(function (resolve, reject) {
            const timeoutFn = setTimeout(reject, timeout);
            if (socket !== innerSocket) {
                debugger;
            }
            innerSocket.emit('timesync', data, () => {
                clearTimeout(timeoutFn);
                resolve();
            });
        });

    socket.on('timesync', data => ts.receive(undefined, data)); // undefined here causes the issue
    ts.on('change', onOffsetChange);

    return ts;
}

Here's what happens in order to cause the issue:

  1. I visit a page where this code gets executed
  2. My server becomes unresponsive while the timesync requests are still pending (I update my server code and it's restarted for example)
  3. Upon reconnection, the debugger statement fires.

The issue is that when I call ts.receive with the first parameter as undefined, undefined is getting sent through to my send callback as the first parameter.

Unfortunately, I can't just replace undefined with socket. This causes an infinite loop for some reason that I've yet to determine. The solution is simple though:

Just ignore the first parameter of the send callback, and always outside-defined socket. This code works perfectly:

export function createTimesyncClient({socket, onOffsetChange}){
    const ts = timesync.create({server: socket});

    ts.send = (_, data, timeout) =>
        new Promise(function (resolve, reject) {
            const timeoutFn = setTimeout(reject, timeout);
            socket.emit('timesync', data, () => { // Note socket here is now coming from the containing closure
                clearTimeout(timeoutFn);
                resolve();
            });
        });

    socket.on('timesync', data => ts.receive(data));
    ts.on('change', onOffsetChange);

    return ts;
}

Either the documentation should be updated, as it currently suffers from this exact issue, or the library code needs to be fixed. Potentially by having receive default its "from" argument to the server passed in through the options.

Though, again... I did run into an infinite loop by attempting to solve it myself. I connected, closed the server (while requests were still pending), waited a second, and then reopened the server. At that point the timesync client endlessly attempted to emit timesync requests.

The infinite loop issue described above is in fact this issue: #11