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.
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.
However, the connection between a client and your WebSocket app closes when no traffic is sent between them for 60 seconds.
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.
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.
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)
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With