12

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);
      }
      
  • iPad 1
    • ファイルの HTTP GET リクエストが来て、バイト範囲を要求する[0; <almost the end of the file>]
    • サーバーが応答し、ファイルのストリーミングを開始します
    • iPad は 1 つまたは 2 つのチャンクを取得すると、一方的にサーバーからのバイトの受け入れを停止することを決定し、ファイルの次のチャンクに対して別のGET要求を発行します。新しい範囲の境界は、たとえば[100, almost the end of the file]. 動画は正常に表示されます。
    • サイクルはステップ 2 から再び繰り返されます。左境界は常にファイルの終わりに向かって移動します。

接続がどのように正確に終了するかは調査しませんでした。iPad が TCP ACK パケットの送信を停止する可能性がありますが、これはそれほど問題ではないと思います。

私の問題は、接続が終了するたびにjava.net.SocketException: Broken pipe例外が発生することです。これはログを汚染するだけでなく (これはマイナー/解決可能な問題です)、例外を発生させると非常にコストがかかるため、パフォーマンスが低下する可能性があると思います。単純なビデオを見ると、例外レートは 1 秒あたり約 1 例外でしたが、サーバーに 100 人の同時ユーザーがいる場合、JVM は実際の作業を行う代わりに、スタック トレースの計算だけに多くの時間を費やしている可能性があります。

また、iOS 6 を使用して iPhone でこれをテストしたところ、iPad 1 と同じ動作を観察できました。繰り返しますが、これは、Samsung Android や、デスクトップ Mac の Safari を含む、私が試したデスクトップ ブラウザーでは発生しません。

質問:

  • これは iPad/iPhone の既知のバグ/機能ですか?
  • これに対する回避策はありますか?
4

4 に答える 4

3

IIRC、「壊れたパイプ」は、読み取り側を閉じた後に反対側がデータを受信したことを意味します。

私が考えることができる最も合理的なことは、視聴されないビデオをダウンロードするための大量の帯域幅を無駄にしないための試みであるということです (おそらく、彼らは通信事業者と合意したものであり、これが「ライブ ストリーミング」の制限の背後にある理由であると思われます)。

「セルラー ネットワークを介した 10 分を超えるビデオ ストリーミング コンテンツは、HTTP ライブ ストリーミングを使用し、ベースラインの 64 kbps オーディオのみの HTTP ライブ ストリームを含める必要があります。」

ダウンロードを抑制する唯一の簡単な方法は、停止read()して受信ウィンドウがいっぱいになるのを待つことですが、これは必ずしも簡単なことではNSURLConnectionありません (たとえば、実際には簡単ではありません)。

非常に幸運な場合、クライアントは書き込み側を閉じ (サーバーがread()EOF になるように)、読み取り側を閉じる前に少し待機します。この場合、クライアントがダウンロードの残りを必要としていないと想定しても安全かもしれません。RFC 2616は少しあいまいですが (ソケットを一方向にしか閉じることができないことを忘れているようです)、「グレースフル クローズ」について言及しています ( Microsoft によると、書き込み側を閉じて、タイムアウトが経過するまで読み取り側からの読み取りを終了する必要があります)。しかしまた言う

ネットワークまたはクライアントの障害が疑われる場合を除き、サーバーは応答の送信中に接続を閉じるべきではありません。

したがって、それが iDevice であることがわかっていて、EOF を読み取った場合、何も壊さないことを徹底的にテストした場合、サーバーがソケットを閉じても安全である可能性があります— User-Agent に応じて HTTP の動作を変更すると、ひどい考えです。

あるいは、気にしないでください。UA スニッフィングを実行し、それが iDevice の場合は例外を無視できます (これは、HTTP の動作を変更するよりもそれほどひどいものではないようです)。例外のオーバーヘッドはほぼ確実に無視できる程度であり、おそらくログに出力するオーバーヘッドよりもはるかに低いでしょう。1 秒間に 100 の例外はありません。よくわからない場合はプロファイルしてください。

Apple にバグを報告することもできますが、これらのことが進むにつれて、特に疑わしいネットワーク動作ではありません。

于 2012-10-09T00:29:45.783 に答える
1

私たちの共有された発見に戻ってきます。アップルのウェブサイトでこの議論を見てください。現在、この問題により、iOS6がストリーミング中に大量のデータを消費するという問題が発生しているようです。

(オランダ語ですが、ここで報告されているのとまったく同じ問題です。Androidは1つのリクエストを実行し、iOSは複数のリクエストを実行します)

iOS6.0.1でこのようなものを再テストして、範囲リクエストの問題が実際に修正されたかどうかを確認します。

私のiPodTouch第5世代でテストしたばかり-iOS6.0.1:範囲要求はまだ0-1を要求しており、その後数回0-フルファイルに続いてより小さな範囲を要求しています。しかし、それでも厄介に見えます

于 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 は、フロンティア通信を行うことができます。

私がこれを言っているのは、OS での JVM の実装が原因で、オペレーティング システムでさえ JVM が適切な TCP 通信のシャットダウンを受信しない理由である可能性があるためです。

于 2012-10-08T22:54:32.787 に答える