我正在测试一个支持HTTP 字节范围请求的HTTP servlet 实现(由 BalusC 共享)。
我发现了不同 HTTP 客户端之间的一些特殊差异,并且想知道我是否没有遗漏任何东西。我在测试中使用了 >2G 的 mp4 视频文件,并且正在使用 Wireshark 捕获数据包。大致是这样的:
三星盖乐世 SII:
- 对文件的 HTTP GET 请求来了,要求字节范围
[0; <almost the end of the file>]
- 服务器响应,开始流式传输文件
每个后续块都在同一 HTTP 响应的范围内提供服务。不会发送新的 HTTP 请求(除非视频被快速转发到某个位置)。流式代码块非常简单,它通过以下方式读取
RandomAccessFile input
和写入:OutputStream output
byte[] buffer
while ((read = input.read(buffer)) > 0) { output.write(buffer, 0, read); }
- 对文件的 HTTP GET 请求来了,要求字节范围
- iPad 1
- 对文件的 HTTP GET 请求来了,要求字节范围
[0; <almost the end of the file>]
- 服务器响应,开始流式传输文件
- iPad 获得一两个块,然后单方面决定停止从服务器接受字节,并发出对文件下一个块的单独
GET
请求。新的范围边界是例如[100, almost the end of the file]
。视频显示正常。 - 循环从第 2 步开始再次重复。左边界总是向文件末尾移动。
- 对文件的 HTTP GET 请求来了,要求字节范围
我没有调查连接到底是如何终止的。可能是 iPad 停止发送 TCP ACK 数据包,我想这并不重要。
我的问题是,对于每个终止的连接,我都会遇到java.net.SocketException: Broken pipe
异常。这不仅会污染日志(这是一个小问题/可解决的问题),而且我相信这会损害性能,因为引发异常非常昂贵。观看简单视频时,异常率约为 1 个异常/秒,但如果服务器有 100 个并发用户,那么 JVM 可能会花费大量时间来计算堆栈跟踪,而不是进行实际工作。
我还在使用 iOS 6 的 iPhone 上对此进行了测试,并且能够观察到与 iPad 1 相同的行为。重申一下,这不会发生在三星 Android 或我尝试过的任何桌面浏览器上,包括桌面 Mac 上的 Safari。
问题:
- 这是 iPad/iPhone 的已知错误/功能吗?
- 有解决方法吗?