HTTP バイト範囲リクエストをサポートするHTTP サーブレット実装 (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 は 1 つまたは 2 つのチャンクを取得すると、一方的にサーバーからのバイトの受け入れを停止することを決定し、ファイルの次のチャンクに対して別の
GET
要求を発行します。新しい範囲の境界は、たとえば[100, almost the end of the file]
. 動画は正常に表示されます。 - サイクルはステップ 2 から再び繰り返されます。左境界は常にファイルの終わりに向かって移動します。
- ファイルの HTTP GET リクエストが来て、バイト範囲を要求する
接続がどのように正確に終了するかは調査しませんでした。iPad が TCP ACK パケットの送信を停止する可能性がありますが、これはそれほど問題ではないと思います。
私の問題は、接続が終了するたびにjava.net.SocketException: Broken pipe
例外が発生することです。これはログを汚染するだけでなく (これはマイナー/解決可能な問題です)、例外を発生させると非常にコストがかかるため、パフォーマンスが低下する可能性があると思います。単純なビデオを見ると、例外レートは 1 秒あたり約 1 例外でしたが、サーバーに 100 人の同時ユーザーがいる場合、JVM は実際の作業を行う代わりに、スタック トレースの計算だけに多くの時間を費やしている可能性があります。
また、iOS 6 を使用して iPhone でこれをテストしたところ、iPad 1 と同じ動作を観察できました。繰り返しますが、これは、Samsung Android や、デスクトップ Mac の Safari を含む、私が試したデスクトップ ブラウザーでは発生しません。
質問:
- これは iPad/iPhone の既知のバグ/機能ですか?
- これに対する回避策はありますか?