Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WebSocket Connection timeout

I am trying to implement failsafe websocket wrapper. And the problem that I have is dealing with timeout errors. The logic should be: if the socket is not opened during $timeoutInMiliseconds - it must be closed and reopened $N times.

I am writing something like this.

var maxReconects = 0;
var ws = new WebSocket(url);
var onCloseHandler = function() {
    if ( maxReconects < N ) {
        maxReconects++;
        // construct new Websocket 
        ....
    }
};
ws.onclose = onCloseHandler;
var timeout = setTimeout(function() {
                console.log("Socket connection timeout",ws.readyState);
                timedOut = true;
                ws.close();  <--- ws.readyState is 0 here 
                timedOut = false;
},timeoutInMiliseconds); 

But the problem is handling timeout websockets right way - if i am trying to close nonconnected socket I receive warning in chrome :

"WebSocket connection to 'ws://127.0.0.1:9010/timeout' failed: WebSocket is closed before the connection is established."

And I have no Idea how to avoid it - ws interface has no abort function .

The other aproach I have tried is not to close socket on timeout if it nonconnected but just mark it as not used more and close it if it receive readyState more than one - but it can produce possible leaks , and to complicated for such simple task.

like image 713
Oleg Avatar asked Apr 26 '15 18:04

Oleg


People also ask

Does WebSocket have timeout?

A WebSocket times out if no read or write activity occurs and no Ping messages are received within the configured timeout period. The container enforces a 30-second timeout period as the default. If the timeout period is set to -1 , no timeout period is set for the connection.

How long do WebSocket connections last?

However, the connection between a client and your WebSocket app closes when no traffic is sent between them for 60 seconds.

Why is WebSocket disconnecting?

WebSocket disconnects can happen by choice, or due to exceptional/error conditions. Here is some information about what happens in each case: Clean disconnect: During a clean disconnect, the user or application will initiate a disconnect sequence.


2 Answers

I've written the following code for opening a websocket failsafe with timeout and retries, see comments in code for more details.

Usage - opening a websocket with 5000ms timeout and 10 retries (maximum):

initWebsocket('ws:\\localhost:8090', null, 5000, 10).then(function(socket){
    console.log('socket initialized!');
    //do something with socket...

    //if you want to use the socket later again and assure that it is still open:
    initWebsocket('ws:\\localhost:8090', socket, 5000, 10).then(function(socket){
        //if socket is still open, you are using the same "socket" object here
        //if socket was closed, you are using a new opened "socket" object
    }

}, function(){
    console.log('init of socket failed!');
});

method initWebsocket() somewhere defined in a library or similar:

/**
 * inits a websocket by a given url, returned promise resolves with initialized websocket, rejects after failure/timeout.
 *
 * @param url the websocket url to init
 * @param existingWebsocket if passed and this passed websocket is already open, this existingWebsocket is resolved, no additional websocket is opened
 * @param timeoutMs the timeout in milliseconds for opening the websocket
 * @param numberOfRetries the number of times initializing the socket should be retried, if not specified or 0, no retries are made
 *        and a failure/timeout causes rejection of the returned promise
 * @return {Promise}
 */
function initWebsocket(url, existingWebsocket, timeoutMs, numberOfRetries) {
    timeoutMs = timeoutMs ? timeoutMs : 1500;
    numberOfRetries = numberOfRetries ? numberOfRetries : 0;
    var hasReturned = false;
    var promise = new Promise((resolve, reject) => {
        setTimeout(function () {
            if(!hasReturned) {
                console.info('opening websocket timed out: ' + url);
                rejectInternal();
            }
        }, timeoutMs);
        if (!existingWebsocket || existingWebsocket.readyState != existingWebsocket.OPEN) {
            if (existingWebsocket) {
                existingWebsocket.close();
            }
            var websocket = new WebSocket(url);
            websocket.onopen = function () {
                if(hasReturned) {
                    websocket.close();
                } else {
                    console.info('websocket to opened! url: ' + url);
                    resolve(websocket);
                }
            };
            websocket.onclose = function () {
                console.info('websocket closed! url: ' + url);
                rejectInternal();
            };
            websocket.onerror = function () {
                console.info('websocket error! url: ' + url);
                rejectInternal();
            };
        } else {
            resolve(existingWebsocket);
        }

        function rejectInternal() {
            if(numberOfRetries <= 0) {
                reject();
            } else if(!hasReturned) {
                hasReturned = true;
                console.info('retrying connection to websocket! url: ' + url + ', remaining retries: ' + (numberOfRetries-1));
                initWebsocket(url, null, timeoutMs, numberOfRetries-1).then(resolve, reject);
            }
        }
    });
    promise.then(function () {hasReturned = true;}, function () {hasReturned = true;});
    return promise;
};

a better solution would be encapsulating the functionality in an own class FailsafeWebsocket or whatsoever. However this solution is sufficient in my project - maybe it helps somebody else too.

like image 78
klues Avatar answered Sep 24 '22 02:09

klues


Your timeout variable is assigned to the setTimeout(... function, which is being invoked lexically. By the time your code gets to var onCloseHander = ... your timeout function has already been invoked.

A solution to this is to make a makeTimeout function:

var makeTimeout = function (timeoutInMiliseconds) {
    return window.setTimeout(function() {
        // do stuff
    }, timeoutInMiliseconds)
};

var timeoutId = makeTimeout(100) will invoke the setTimeout function and set timeoutId as the value of the timeout ID. If you need to cancel this timeout, that can be done by invoking window.clearTimeout(timeoutId).

like image 43
amarpatel Avatar answered Sep 21 '22 02:09

amarpatel