8

我已经实现了一个长轮询连接,以允许我在前端使用 Tomcat Web 服务器和标准 JavaScript 进行服务器端推送(彗星)。为了保持连接继续,我有一个简单的保持活动循环,一旦最后一个请求完成/失败,就会启动一个新请求。

在绝大多数情况下,这种连接工作得非常好,并且像我预期的那样保持活力。但是,我注意到当用户的 Internet 连接中断时(例如,他们从 VPN 断开连接、拔下以太网等)并且我有一个待处理的 XMLHttpRequest 到服务器,我没有收到任何失败的迹象。正因为如此,连接无声无息地消失了,除非我不断地向服务器发送请求以测试连接(这似乎违背了使用长轮询的目的),否则我无法知道它是否发生了。

这是我在 Chrome 中看到的请求对象,当它在这种无声的死亡中死去时:

request: XMLHttpRequest
    onabort: function ()
    onerror: function ()
    onload: null
    onloadend: null
    onloadstart: null
    onprogress: null
    onreadystatechange: function ()
    readyState: 1
    response: ""
    responseText: ""
    responseType: ""
    responseXML: null
    status: [Exception: DOMException]
    statusText: [Exception: DOMException]
    upload: XMLHttpRequestUpload
    withCredentials: false

我设置了三个侦听器(onabort、onerror、onreadystatechange)以在它们被解雇时发出警报,但每当我与服务器的连接中断时,我什么也得不到。这是我形成请求的方式:

var request = new XMLHttpRequest();

//url is just the url to my servlet to handle this
request.open("GET", url, true);

//handlestatechange is just my standard handling code 
//that I've put an alert at the top of
request.onreadystatechange = handleStateChange;

request.onerror = function()
{
    alert("We encountered an error");
}

request.onabort = function()
{
    alert("I've had an abortion");
}

request.send(null);

看起来这将是一个非常标准的情况,但我还没有看到任何关于如何允许长轮询连接从这种断开连接中恢复的对话。

难道我做错了什么?是否有其他更标准的方法来绕过这个问题进行长轮询/彗星?

对此的任何帮助将不胜感激,谢谢

4

2 回答 2

2

我相信处理这个问题的最好方法是将长轮询请求的持续时间限制在一段时间内T(例如 60 秒是一个不合理的值),然后在客户端使用超时来检测停滞状态。理想情况下,您应该使用 XHRtimeout属性来执行此操作,但它不受支持 x-browser,尽管它在 W3C 规范中。因此,您需要使用setTimeoutand来实现您自己的支持xhr.abort()

然后,客户端可以假设如果他们在 T 秒内没有得到响应,则连接已停止,并且可以中止当前请求并尝试重新连接。

这工作得相当好,但确实意味着在检测客户端的连接何时停止时存在一些延迟(最多 T 秒)。对于长轮询请求,我不知道有很多事情要做。如果您确实需要更好的解决方案,那么您可能需要查看发送回心跳消息或WebSockets的流式彗星连接。

这就是简单的答案。不幸的是,这对于各种奇怪的边缘情况来说都是一个不小的问题。例如,您可能关心也可能不关心具有连接超时的代理服务器,或者可能会在用户计算机上更改默认 TCP 超时的各种方式。这就是为什么我们看到像Socket.IO这样的框架隐藏了很多这种丑陋之处。

PS 可能值得注意的是,您可以通过 监控某些浏览器中的在线状态navigator.onLine,但它的支持很差。它真正运行正常的唯一浏览器是 Chrome。它唯一真正告诉您的是用户是否因某种原因而处于离线状态。它不能告诉你他们有一个有效的联系。

于 2013-01-08T14:34:52.387 回答
0

解决方案是启用 TCP keepalive。它是 TCP 中的一种机制,用于在失去可达性时关闭连接。您的应用程序不需要修改。您可以在操作系统或服务器应用程序中启用保活。

于 2018-08-28T13:20:42.003 回答