15

UPnP 経由で XBox に音楽をストリーミングする Android アプリを試しています。ストリーミングはほとんどの場合機能しますが、特にネットワーク上で他のアクティビティがある場合は、1 ~ 2 分後にストリーミングが停止することが非常に頻繁にあります。これは、他の XBox 以外のデバイスにストリーミングする場合には発生しません。さまざまな UPnP サーバー アプリでこの動作を確認しました。

多くの Wireshark トレースを分析した結果、根本原因を突き止めました。XBox で TCP レシーバー ウィンドウがいっぱいになった後、1 バイトのペイロード データを含むゼロ ウィンドウ プローブに応答して、ウィンドウの更新を明示的に再アナウンスするだけのようです。

Windows ベースのマシンは 1 バイトのペイロードを含むゼロ ウィンドウ プローブを送信しますが、Linux ベースのマシンは 0 バイトのペイロード (純粋な ACK) を含むプローブを送信します。

理想的なネットワーク条件の下では、これは問題ではありません。なぜなら、受信者は、愚かなウィンドウ シンドロームを回避するために、ウィンドウ内に十分なスペースが解放されると、常に単一のウィンドウ更新 ACK メッセージを送信するからです。ただし、その 1 つの Window Update パケットが失われると、Linux ベースの Android デバイスに再度応答することはありません。これは、これらのデバイスの TCP スタックが 0 バイトのペイロードでゼロ ウィンドウ プローブを使用するためです (Wirehsark へのキープ アライブ パケットのように見えます)。 )。

XBox と WMP 間の TCP ストールは次のようになります。


   4966 92.330358   10.0.2.214            10.0.2.133            TCP      [TCP ZeroWindow] 27883 > 10243 [ACK] Seq=183 Ack=1723007 Win=0 Len=0
   4971 92.648068   10.0.2.133            10.0.2.214            TCP      [TCP ZeroWindowProbe] 10243 > 27883 [ACK] Seq=1723007 Ack=183 Win=64240 Len=1
   4972 92.649009   10.0.2.214            10.0.2.133            TCP      [TCP ZeroWindowProbeAck] [TCP ZeroWindow] 27883 > 10243 [ACK] Seq=183 Ack=1723007 Win=0 Len=0
   4977 93.256579   10.0.2.133            10.0.2.214            TCP      [TCP ZeroWindowProbe] 10243 > 27883 [ACK] Seq=1723007 Ack=183 Win=64240 Len=1
   4978 93.263118   10.0.2.214            10.0.2.133            TCP      [TCP ZeroWindowProbeAck] [TCP ZeroWindow] 27883 > 10243 [ACK] Seq=183 Ack=1723007 Win=0 Len=0
   4999 94.310534   10.0.2.214            10.0.2.133            TCP      [TCP Window Update] 27883 > 10243 [ACK] Seq=183 Ack=1723007 Win=16384 Len=0

Xbox が Zero Window Probe パケットにアクティブに応答していることに注意してください。

XBox と Android クライアント間の通常の TCP ストールは次のようになります。


7099 174.844077  10.0.2.214            10.0.2.183            TCP [TCP ZeroWindow] [TCP ACKed lost segment] 20067 > ssdp [ACK] Seq=143 Ack=2962598 Win=0 Len=0
 7100 175.067981  10.0.2.183            10.0.2.214            TCP [TCP Keep-Alive|TCP Keep-Alive] ssdp > 20067 [ACK] Seq=2962597 Ack=143 Win=6912 Len=0
 7107 175.518024  10.0.2.183            10.0.2.214            TCP [TCP Keep-Alive|TCP Keep-Alive] ssdp > 20067 [ACK] Seq=2962597 Ack=143 Win=6912 Len=0
 7108 175.894079  10.0.2.214            10.0.2.183            TCP [TCP Window Update] 20067 > ssdp [ACK] Seq=143 Ack=2962598 Win=16384 Len=0

XBox はKeepAlive パケットに応答しないことに注意してください。

XBox と Android デバイス間の TCP ストールは、最初の Window Update アナウンスが見逃された場合、次のようになります。


 7146 175.925019  10.0.2.214            10.0.2.183            TCP [TCP ZeroWindow] 20067 > ssdp [ACK] Seq=143 Ack=3000558 Win=0 Len=0
 7147 176.147901  10.0.2.183            10.0.2.214            TCP [TCP Keep-Alive|TCP Keep-Alive] ssdp > 20067 [ACK] Seq=3000557 Ack=143 Win=6912 Len=0
 7155 176.597820  10.0.2.183            10.0.2.214            TCP [TCP Keep-Alive|TCP Keep-Alive] ssdp > 20067 [ACK] Seq=3000557 Ack=143 Win=6912 Len=0
 7165 177.498087  10.0.2.183            10.0.2.214            TCP [TCP Keep-Alive|TCP Keep-Alive] ssdp > 20067 [ACK] Seq=3000557 Ack=143 Win=6912 Len=0
 7218 179.297763  10.0.2.183            10.0.2.214            TCP [TCP Keep-Alive|TCP Keep-Alive] ssdp > 20067 [ACK] Seq=3000557 Ack=143 Win=6912 Len=0
 7297 182.897804  10.0.2.183            10.0.2.214            TCP [TCP Keep-Alive|TCP Keep-Alive] ssdp > 20067 [ACK] Seq=3000557 Ack=143 Win=6912 Len=0
 7449 190.097780  10.0.2.183            10.0.2.214            TCP [TCP Keep-Alive|TCP Keep-Alive] ssdp > 20067 [ACK] Seq=3000557 Ack=143 Win=6912 Len=0
 7759 204.498070  10.0.2.183            10.0.2.214            TCP [TCP Keep-Alive|TCP Keep-Alive] ssdp > 20067 [ACK] Seq=3000557 Ack=143 Win=6912 Len=0
 8412 233.298081  10.0.2.183            10.0.2.214            TCP [TCP Keep-Alive|TCP Keep-Alive] ssdp > 20067 [ACK] Seq=3000557 Ack=143 Win=6912 Len=0
 9617 290.898134  10.0.2.183            10.0.2.214            TCP [TCP Keep-Alive|TCP Keep-Alive] ssdp > 20067 [ACK] Seq=3000557 Ack=143 Win=6912 Len=0
11326 358.047838  10.0.2.214            10.0.2.183            TCP      20067 > ssdp [FIN, ACK] Seq=143 Ack=3000558 Win=16384 Len=0

XBox は開いているウィンドウを再通知することはなく、最終的に接続を終了することに注意してください。

小さなパケット注入プログラムを作成して、私の理論を確認しました。失速したら、手作りの TCP Zero Window Probe パケットを起動できます。これを行うと、XBox は即座に元に戻り、通常どおり続行します。残念ながら、アプリケーションからこれを行うことはできません。そのようなパケットを作成するには CAP_NET_RAW 機能が必要であり、それをアプリケーションに付与することができないからです。

これは、手動で挿入されたゼロ ウィンドウ プローブ (パケット 7258) を使用した上記のケースです。正しい seq/ack 番号は必要ありません。必要なのは 1 バイトのデータだけです。


   7253 373.274394  10.0.2.214            10.0.2.186            TCP      [TCP ZeroWindow] 39378 > ssdp [ACK] Seq=3775184695 Ack=1775679761 Win=0 Len=0
   7254 375.367317  10.0.2.186            10.0.2.214            TCP      [TCP Keep-Alive] ssdp > 39378 [ACK] Seq=1775679760 Ack=3775184695 Win=3456 Len=0
   7255 379.562480  10.0.2.186            10.0.2.214            TCP      [TCP Keep-Alive] ssdp > 39378 [ACK] Seq=1775679760 Ack=3775184695 Win=3456 Len=0
   7256 387.953095  10.0.2.186            10.0.2.214            TCP      [TCP Keep-Alive] ssdp > 39378 [ACK] Seq=1775679760 Ack=3775184695 Win=3456 Len=0
   7257 404.703312  10.0.2.186            10.0.2.214            TCP      [TCP Keep-Alive] ssdp > 39378 [ACK] Seq=1775679760 Ack=3775184695 Win=3456 Len=0
   7258 406.571301  10.0.2.186            10.0.2.214            TCP      [TCP ACKed lost segment] [TCP Retransmission] ssdp > 39378 [ACK] Seq=1 Ack=1 Win=1 Len=1
   7259 406.603512  10.0.2.214            10.0.2.186            TCP      39378 > ssdp [ACK] Seq=3775184695 Ack=1775679761 Win=16384 Len=0

TCP Seq/Ack 番号が正しくないため、Wireshark はパックを無効な ACK を使用したわがままなデータ送信と解釈しますが、それでも XBox は元の状態に戻り、ストリーミングを再開します。

  • デバイスをルート化することなく、Android アプリで CAP_NET_RAW 機能を取得する方法はありますか?
  • Linux TCP レイヤーに 1 バイトのペイロード データを含むゼロ ウィンドウ プローブを強制的に送信させるために使用できる他のトリックはありますか?
  • XBox の TCP スタックをウェイクアップできる、他に試してみることができるあいまいな TCP オプションはありますか?
  • XBoxに別のウィンドウ更新を送信するよう説得するための他の帯域外アプローチはありますか?
  • 私が検討できる他の完全に無関係なアプローチはありますか?

編集:これは、提供された提案が機能しない理由の説明です。

  1. TCP_NODELAYウィンドウが開いている間のパケットの送信方法にのみ影響します。具体的には、このオプションを設定すると、MSS をいっぱいにする TCP パケットを作成しようとして、TCP スタックが追加のデータを数ミリ秒待機することがなくなります。受信ウィンドウが閉じているときは、データを送信できません。

  2. TCP_QUICKACKホストが受信しているパケットを ACK する方法に影響します。私が直面している問題は、送信者が受信しているパケットを ACK する方法を変更する必要があることです。

  3. MSG_OOBTCP緊急フラグのみを設定します。緊急データは、ウィンドウ処理に関する限り、まったく異なる方法で処理されず、受信側のウィンドウが閉じられている場合でも送信されません。

  4. TCP 輻輳制御アルゴリズムを変更しても効果はありません。XBox は強制的にデータ送信速度を MP3 の再生速度に制限しているため、輻輳ウィンドウがいっぱいになるのを避けることは事実上不可能です。スループットを推測することで輻輳ウィンドウを減らすことは可能かもしれませんが、これは輻輳ウィンドウがいっぱいになる可能性を減らすだけで、完全に防ぐことはできません。

  5. UPnP スタックを使用する必要があり、UPnP は HTTP 経由でデータを配信するため、TCP 経由でデータを配信するため、UDP の使用はオプションではありません。

4

3 に答える 3

3

私は役立つかもしれないいくつかのことを見つけました:

  1. TCPioctl(2) TCP_NODELAYにより、カーネルはすぐに PSH パケットを送信します。接続が切れる場合があります。

  2. TCPioctl(2) TCP_QUICKACKは ACK パケットで何かおかしなことをします。接続が切れる場合があります。

  3. 使用するsend(2)と、フラグを設定できますMSG_OOB。これにより、XBox が目の前に突き刺さり、注意を引くことができ、最初からやり直すことができます。CISCO は、さまざまなプラットフォームが TCP URG にどのように応答するかについての優れた要約を書きました。彼らのアドバイスは、URG の使用を避けることですが、うまくいくかもしれません。

  4. TCP ソケット オプションTCP_CONGESTIONを使用すると、さまざまな輻輳回避アルゴリズムを選択できます。そもそも、いっぱいになったウィンドウを回避するのに役立つものを見つけることができるでしょうか? (少なくとも TCP Vegas はモジュールとして実装されており、Android プラットフォームのデフォルトの輻輳回避アルゴリズムから変更できない可能性があります。)

于 2011-01-30T09:16:10.440 に答える
2

実際、あなたは Linux のバグに遭遇しています。ゼロ ウィンドウの状況を処理する場合、Linux は RFC793 に準拠していません。Windowsは実際に正しいことをしています。RFC 793 では、受信者が未承諾のウィンドウ更新メッセージを送信する必要がないことに注意してください。代わりに、送信者が少なくとも 1 オクテットのデータを含むウィンドウ プローブを送信する必要があります。

于 2013-01-14T17:24:46.703 に答える
0

TCP の代わりに UDP の使用を検討することをお勧めします。オーディオのコピーをローカルに作成するのではなく、Xbox でオーディオを再生することを想定していますか? その場合、すべてのパケットを確実に取得するかどうかは本当に気にしません。パケット伝送の信頼性は、TCP で得られるオーバーヘッドですが、実際には必要ないかもしれません。UDP ははるかに単純で、ストリーミングの状況でより一般的です。

于 2011-01-29T21:06:29.817 に答える