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に別のウィンドウ更新を送信するよう説得するための他の帯域外アプローチはありますか?
- 私が検討できる他の完全に無関係なアプローチはありますか?
編集:これは、提供された提案が機能しない理由の説明です。
TCP_NODELAY
ウィンドウが開いている間のパケットの送信方法にのみ影響します。具体的には、このオプションを設定すると、MSS をいっぱいにする TCP パケットを作成しようとして、TCP スタックが追加のデータを数ミリ秒待機することがなくなります。受信ウィンドウが閉じているときは、データを送信できません。TCP_QUICKACK
ホストが受信しているパケットを ACK する方法に影響します。私が直面している問題は、送信者が受信しているパケットを ACK する方法を変更する必要があることです。MSG_OOB
TCP緊急フラグのみを設定します。緊急データは、ウィンドウ処理に関する限り、まったく異なる方法で処理されず、受信側のウィンドウが閉じられている場合でも送信されません。TCP 輻輳制御アルゴリズムを変更しても効果はありません。XBox は強制的にデータ送信速度を MP3 の再生速度に制限しているため、輻輳ウィンドウがいっぱいになるのを避けることは事実上不可能です。スループットを推測することで輻輳ウィンドウを減らすことは可能かもしれませんが、これは輻輳ウィンドウがいっぱいになる可能性を減らすだけで、完全に防ぐことはできません。
UPnP スタックを使用する必要があり、UPnP は HTTP 経由でデータを配信するため、TCP 経由でデータを配信するため、UDP の使用はオプションではありません。