12

我正在测试一个支持HTTP 字节范围请求的HTTP servlet 实现(由 BalusC 共享)

我发现了不同 HTTP 客户端之间的一些特殊差异,并且想知道我是否没有遗漏任何东西。我在测试中使用了 >2G 的 mp4 视频文件,并且正在使用 Wireshark 捕获数据包。大致是这样的:

  • 三星盖乐世 SII:

    • 对文件的 HTTP GET 请求来了,要求字节范围[0; <almost the end of the file>]
    • 服务器响应,开始流式传输文件
    • 每个后续块都在同一 HTTP 响应的范围内提供服务。不会发送新的 HTTP 请求(除非视频被快速转发到某个位置)。流式代码块非常简单,它通过以下方式读取RandomAccessFile input和写入:OutputStream outputbyte[] buffer

      while ((read = input.read(buffer)) > 0) {
          output.write(buffer, 0, read);
      }
      
  • iPad 1
    • 对文件的 HTTP GET 请求来了,要求字节范围[0; <almost the end of the file>]
    • 服务器响应,开始流式传输文件
    • iPad 获得一两个块,然后单方面决定停止从服务器接受字节,并发出对文件下一个块的单独GET请求。新的范围边界是例如[100, almost the end of the file]。视频显示正常。
    • 循环从第 2 步开始再次重复。左边界总是向文件末尾移动。

我没有调查连接到底是如何终止的。可能是 iPad 停止发送 TCP ACK 数据包,我想这并不重要。

我的问题是,对于每个终止的连接,我都会遇到java.net.SocketException: Broken pipe异常。这不仅会污染日志(这是一个小问题/可解决的问题),而且我相信这会损害性能,因为引发异常非常昂贵。观看简单视频时,异常率约为 1 个异常/秒,但如果服务器有 100 个并发用户,那么 JVM 可能会花费大量时间来计算堆栈跟踪,而不是进行实际工作。

我还在使用 iOS 6 的 iPhone 上对此进行了测试,并且能够观察到与 iPad 1 相同的行为。重申一下,这不会发生在三星 Android 或我尝试过的任何桌面浏览器上,包括桌面 Mac 上的 Safari。

问题:

  • 这是 iPad/iPhone 的已知错误/功能吗?
  • 有解决方法吗?
4

4 回答 4

3

IIRC,“断管”仅表示另一方在关闭其读取端后接收到数据。

我能想到的最合理的事情是,它试图不浪费大量带宽下载从未被观看的视频(也许他们已经与运营商达成一致,我怀疑这是“直播”限制背后的原因:

“蜂窝网络上超过 10 分钟的视频流内容必须使用 HTTP 实时流,并包括基线 64 kbps 纯音频 HTTP 实时流。”

限制下载的唯一其他简单方法是停止read()并等待接收窗口填满,但这并不总是容易做到的(NSURLConnection例如,真的并不容易做到这一点)。

如果您非常幸运,客户端将关闭其写入端(这样服务器将read()EOF)并等待一段时间,然后再关闭其读取端。在这种情况下,可以安全地假设客户端不再需要下载的其余部分。RFC 2616有点模糊(似乎忘记了套接字只能在一个方向上关闭),但提到了“优雅关闭”(根据微软的说法,这涉及关闭写入端并完成从读取端的读取,直到超时过去),但也说

服务器不应该在传输响应的过程中关闭连接,除非怀疑网络或客户端故障。

因此,如果您知道它是一个 iDevice 并且您阅读了 EOF,那么服务器关闭套接字可能是安全的,前提是您已经彻底测试它不会破坏任何东西——根据 User-Agent 改变 HTTP 行为就像一个可怕的想法。

或者,不要在意。如果它是一个 iDevice(这似乎不如改变 HTTP 行为那么可怕),你可以进行 UA 嗅探并忽略异常。异常开销几乎可以肯定可以忽略不计,并且可能远低于将其打印到日志的开销。每秒 100 个异常不算什么。如果您不确定,请对其进行分析。

您也可以向 Apple 提交错误,但随着这些事情的发展,这并不是特别可疑的网络行为

于 2012-10-09T00:29:45.783 回答
1

回到我们共同的发现。看看苹果网站上的这个讨论。现在看来,这个问题已经导致 iOS6 在流式传输时消耗过多数据的问题。

(使用荷兰语,但这里报告的问题完全相同。Android 执行 1 个请求,iOS 执行多个请求)

是时候用 iOS6.0.1 重新测试这些东西,看看它们是否确实修复了范围请求问题。

刚刚在我的 iPod Touch 第 5 代 - iOS6.0.1 上进行了测试:范围请求仍然请求 0-1,然后是几次 0-full 文件,然后是更小的范围。不过看起来还是很乱

于 2012-11-16T11:59:04.623 回答
1

在 iPhone 上流式传输时,您需要发送 Accept Ranges 标头。这可能会导致您的问题。看看这个帖子

在 iOS 上直接访问 MP4 时播放,但通过 PHP 读取时不播放

于 2012-10-01T21:57:00.977 回答
1

首先,

花大量时间计算堆栈跟踪

如果您不打算调用 get/printStackTrace(),则不会计算。把它从原木上关掉或抓住它并在某处避开它。

我有同样的问题,它也没有消失。好吧,这些确实是愚蠢的选择,但是您可以使用负载平衡器来接受连接并将其重定向到您的服务器 tomcat 或 glassfish,无论您使用什么。当我开始在 AWS 上使用 ELB 时,我观察到了这种缺乏断管行为。NGINX 或 Apache 可以为您做一些前沿通信。

我这样说是因为即使是操作系统也可能是 JVM 没有收到正确的 TCP 通信关闭的原因,因为操作系统上的 JVM 实现。

于 2012-10-08T22:54:32.787 回答