我最近发现,使用 Internet Explorer 运行我的应用程序的用户出现间歇性故障的问题是由于 Internet Explorer 中的错误造成的。该错误位于 HTTP 堆栈中,应该会影响所有使用来自 IE 的 POST 请求的应用程序。结果是失败的特征是请求似乎挂起大约 5 分钟(取决于服务器类型和配置),然后从服务器端失败。服务器放弃后,浏览器应用程序将在 post 请求中出错。我将在下面详细解释 IE 错误。
据我所知,任何使用 XMLHttpRequest 向服务器发送 POST 请求的应用程序都会发生这种情况,如果请求是在错误的时间发送的。我编写了一个示例程序,试图在这些时间发送 POSTS。它尝试在服务器关闭连接的精确时刻向服务器发送连续的 POST。间隔来自服务器发送的 Keep-Alive 标头。
我发现当从 IE 运行到具有一点延迟的服务器时(即不在同一个 LAN 上),问题仅在几次 POST 后出现。发生这种情况时,IE 会锁定得很厉害,以至于必须强制关闭。滴答作响的时钟表明浏览器仍在响应。
您可以通过浏览以下网址进行尝试: http: //pubdev.hitech.com/test.post.php。请注意,当您运行它时,您在任何 IE 会话中都没有任何重要的未保存信息,因为我发现它会使 IE 崩溃。
完整的源代码可以在以下位置检索: http: //pubdev.hitech.com/test.post.php.txt。您可以在任何具有 php 并配置为持久连接的服务器上运行它。
我的问题是:
其他人在这个问题上的经验是什么?
是否有解决此问题的已知策略(“使用其他浏览器”除外)?
Microsoft 是否有比我找到的文章更好的信息(见下文)?
问题是 Web 浏览器和服务器默认使用 RFC 2616 第 8.1 节中所述的持久连接(参见http://www.ietf.org/rfc/rfc2616.txt)。这对于性能非常重要——尤其是对于 AJAX 应用程序——并且不应该被禁用。然而,有一个小的时间漏洞,浏览器可能会在服务器决定连接空闲并决定关闭它的同时开始在先前使用的连接上发送 POST。结果是浏览器的 HTTP 堆栈将收到一个套接字错误,因为它正在使用一个关闭的套接字。RFC 2616 第 8.1.4 节预见了这种情况,并指出,“......客户端、服务器和代理必须能够从异步关闭事件中恢复。客户端软件应该重新打开传输连接并重新传输中止的请求序列而无需用户交互……”
发生这种情况时, Internet Explorer会重新发送 POST,但它会破坏请求。它发送 POST 标头,包括发布的数据的 Content-Length,但它不发送数据。这是一个不正确的请求,服务器将等待一段未指定的时间来获取承诺的数据,然后再因错误导致请求失败。我已经能够使用模拟 HTTP 服务器的 C 程序在 100% 的情况下演示此失败,该服务器会关闭传入 POST 请求的套接字而不发送响应。
微软似乎在 http://support.microsoft.com/kb/895954中承认了这一失败。他们说它会影响 IE 版本 6 到 9。它为这个问题提供了一个修补程序,自 IE 7 以来的所有版本的 IE 都附带了该修补程序。由于以下原因,该修补程序似乎并不令人满意:
除非您使用 regedit 将名为 FEATURE_SKIP_POST_RETRY_ON_INTERNETWRITEFILE_KB895954 的键添加到注册表,否则它不会启用。这不是我希望我的用户必须做的事情。
该修补程序实际上并不能修复损坏的 POST。相反,如果套接字按照 RFC 的预期关闭,它会立即出错而不会尝试重新发送 POST。应用程序仍然失败——只是失败得更快。
以下示例是一个自包含的 php 程序,用于演示该错误。它尝试在服务器关闭连接的精确时刻向服务器发送连续的 POST。间隔来自服务器发送的 Keep-Alive 标头。