在Internet Explorer中,使用POST请求的AJAX应用程序是否总是失败?

9
我最近发现,使用Internet Explorer运行我的应用程序时出现间歇性故障的问题是由于Internet Explorer中的一个错误导致的。这个错误在HTTP堆栈中,应该会影响所有使用IE发送POST请求的应用程序。结果就是一个请求似乎挂起了大约5分钟(取决于服务器类型和配置),然后从服务器端失败。当服务器放弃后,浏览器应用程序将会在post请求中报错。我将在下面详细解释IE的错误。
据我所知,只要在错误的时刻发送请求,任何使用XMLHttpRequest发送POST请求到服务器的应用程序都会发生这种情况。我编写了一个示例程序,试图在服务器关闭连接的精确时刻连续向服务器发送POST。时间间隔是从服务器发送的Keep-Alive头派生的。
我发现,当从IE运行到具有一定延迟的服务器上(即不在同一局域网上)时,只需几个POSTs就会出现问题。当它发生时,IE会锁定得如此之紧,以至于必须强制关闭。滴答声表明浏览器仍在响应。
您可以通过浏览以下链接来尝试:http://pubdev.hitech.com/test.post.php。请注意,在运行此程序时,请确保您的IE会话中没有任何重要的未保存信息,因为我发现它会导致IE崩溃。
完整源代码可在以下链接中检索:http://pubdev.hitech.com/test.post.php.txt。您可以在任何配置了PHP和持久连接的服务器上运行它。
我的问题是:
  1. 其他人对这个问题有什么经验吗?

  2. 除了“使用另一个浏览器”之外,是否有已知的解决此问题的策略?

  3. 微软是否有比我找到的文章更好的关于这个问题的信息?

问题在于,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请求的套接字而不发送响应。
Microsoft似乎在http://support.microsoft.com/kb/895954中承认了这个问题。他们说它影响IE 6到9版本。提供了解决此问题的热修复程序,该程序已随所有IE版本一起发布自IE 7以来。以下原因使热修复程序似乎不令人满意:
  1. 除非您使用regedit添加名为FEATURE_SKIP_POST_RETRY_ON_INTERNETWRITEFILE_KB895954的密钥到注册表中,否则它不会启用。这不是我希望我的用户必须做的事情。

  2. 热修补程序实际上并没有修复损坏的POST。相反,如果套接字按RFC预期关闭,则立即出错而不尝试重新发送POST。应用程序仍然失败 - 只是失败得更快。

以下示例是一个自包含的PHP程序,演示了该错误。它试图在服务器关闭连接的精确时刻连续发送POST。间隔是从服务器发送的Keep-Alive标头派生的。


你有没有找到解决这个问题的方法?我在使用jQuery的ajax post操作时遇到了同样的问题。 - ClearCloud8
@tony-abo 我遇到了类似的问题。想知道是否有解决方法?另外,为什么GET请求不会出现这种情况?是因为GET没有任何BODY,因此在网络故障时重新发送是成功的吗? - Peter
3个回答

6
我们经常会在IE上遇到这个问题,没有很好的解决方案。唯一可以保证解决问题的方法是确保Web服务器的保持连接超时时间高于浏览器的保持连接超时时间(默认情况下,IE为60秒)。如果Web服务器设置的值较低,则任何情况都可能导致IE尝试重用连接并发送请求,但由于套接字已关闭,该请求被拒绝并带有TCP RST。如果Web服务器的keepalive超时值高于IE的keepalive超时值,则IE对连接的重用确保套接字不会关闭。对于高延迟连接,您必须考虑传输时间为在途时间可能是一个问题。
然而,请记住,增加服务器上的keepalive意味着空闲连接使用服务器套接字更长时间。因此,您可能需要调整服务器以处理大量非活动空闲连接。这可能是一个问题,因为它可能会导致向服务器突发的负载,而服务器无法处理。
还有一件事要记住。您注意到RFC第8.1.4节规定:“......客户端、服务器和代理必须能够从异步关闭事件中恢复过来。客户端软件应该重新打开传输连接并重新传输中止的请求序列,而无需用户交互......”
您忘记了一个非常重要的部分。以下是完整的文本: 客户端软件应该重新打开传输连接并重新传输已中止的请求序列,而无需用户交互,只要请求序列是幂等的(参见第9.1.2节)。不可恢复的方法或序列必须不自动重试,尽管用户代理可以提供人工操作者重试请求(S)的选择。具有对应用程序的语义理解的用户代理软件的确认可能替换用户确认。如果第二个请求序列失败,则不应重复自动重试
根据9.1.2定义,HTTP POST是不可幂等的。因此,注册表修改的行为实际上在RFC中技术上是正确的。

0

不,通常POST在IE中是有效的。你所说的可能是一个问题,但它并不是如此重要,值得写这么长的一篇文章。

当你发出POST ajax请求时,为了确保覆盖每个浏览器的不一致性,只需使用jquery。

还有一件事: 没有理智的人会告诉你“使用另一个浏览器”,因为IE被广泛使用,需要被照顾(好吧,除了IE6和一些较新的版本)

所以,POST必须在IE中工作,但为了使自己免受意外错误行为的影响,使用jquery,你可以安心睡觉。


3
确实出现了这个问题。当我说间歇性时,我的意思是根据服务器的不同,POST请求有1%到10%左右的几率会出现该问题。你怎么能说这不是一个重大问题呢?无论你使用哪个JavaScript库,因为问题在HTTP堆栈中,所以都会收到POST返回的错误。那么你该怎么办呢? - Tony Abo

0

我从未遇到过这个问题。而我们的客户大多使用IE6。

我怀疑你将保持活动状态的计时器配置得太长了。大多数人将其配置为小于1秒,因为持久连接只是为了加速页面加载,而不是服务Ajax调用。

如果您将保持活动状态的时间配置得太长,您将面临比IE崩溃更严重的问题 - 您的服务器将耗尽打开套接字的文件描述符!*

*注意:顺便说一句,打开而不关闭与HTTP服务器的连接是一种众所周知的DOS攻击,试图强制服务器达到其最大打开套接字限制。这就是为什么大多数服务器管理员也会配置连接超时以避免套接字打开时间过长。


保持连接的默认值从Apache 2.0的15秒更改为Apache 2.2的5秒。我没有在运行我的应用程序的服务器上明确更改它们。在我看来,只要下一个请求发生在连接被关闭的时刻,这个问题就会发生在任何保持连接超时设置上。确保它永远不会发生的唯一方法是完全禁用持久连接。 - Tony Abo
你的应用程序发送AJAX POSTS的频率有多高?即使它不经常发送,你的用户是否可能会遇到偶尔出现的问题,导致应用程序似乎冻结了,他们必须重新开始?我的应用程序非常动态,可以根据用户交互发送大量的AJAX请求。如果没有连接池,它将以惊人的速度打开和关闭连接。 - Tony Abo
1
我不记得准确的统计数据,但我认为ajax请求的平均速率大约是每十秒一次。我没有测试过你的代码,但请考虑一下:Facebook使用了大量的Ajax。看看他们的标头,他们启用了保持连接。Facebook是否曾经像你描述的那样崩溃过?有数十亿的用户,如果这真的是一个大问题,肯定会有人注意到的。 - slebetman
1
现在Facebook很有趣。看了一些跟踪信息,它发送一个“Connection: keep-alive”标头,但没有“Keep-Alive”头。似乎服务器从不关闭连接。浏览器似乎关闭其中的一些(使用RST而不是FIN)。也许这就是我的答案。也许我可以设置“KeepAlive On”和“KeepAliveTimeout 0”。 - Tony Abo
FYI,KeepAliveTimeout 0 不起作用。它会导致 Apache 在所有响应上发送 Connection: close 标头。 - Tony Abo
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接