Javascript WebSocket 连接超时

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/29881957/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 04:12:50  来源:igfitidea点击:

WebSocket Connection timeout

javascriptwebsocket

提问by Oleg

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.

我正在尝试实现故障安全 websocket 包装器。我遇到的问题是处理超时错误。逻辑应该是:如果套接字在 $timeoutInMiliseconds 期间没有打开 - 它必须关闭并重新打开 $N 次。

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 :

但问题是正确处理超时 websockets - 如果我试图关闭未连接的套接字,我会在 chrome 中收到警告:

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

“与 'ws://127.0.0.1:9010/timeout' 的 WebSocket 连接失败:在建立连接之前 WebSocket 已关闭。”

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

而且我不知道如何避免它 - ws 接口没有中止功能。

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.

我尝试过的另一种方法是,如果套接字未连接,则在超时时不关闭套接字,而是将其标记为未使用更多,如果它接收到多个 readyState 则关闭它 - 但它可能会产生可能的泄漏,并且对于这种简单的任务来说会变得复杂。

回答by amarpatel

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

您的timeout变量被分配给setTimeout(...正在词法调用的函数。当你的代码到达var onCloseHander = ...你的timeout函数时,你的函数已经被调用了。

A solution to this is to make a makeTimeoutfunction:

对此的解决方案是创建一个makeTimeout函数:

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

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

var timeoutId = makeTimeout(100)将调用该setTimeout函数并设置timeoutId为超时 ID 的值。如果您需要取消此超时,可以通过调用window.clearTimeout(timeoutId).

回答by klues

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

我编写了以下代码,用于打开带有 timeout 和 retries 的 websocket 故障安全,有关更多详细信息,请参阅代码中的注释。

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

用法- 打开具有 5000 毫秒超时和 10 次重试(最大)的 websocket:

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:

initWebsocket()在库或类似的地方定义的方法:

/**
 * 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 FailsafeWebsocketor whatsoever. However this solution is sufficient in my project - maybe it helps somebody else too.

更好的解决方案是将功能封装在自己的类中FailsafeWebsocket或其他任何东西中。然而,这个解决方案在我的项目中已经足够了 - 也许它也可以帮助其他人。