Стек TCP XBox 360 не отвечает на нулевые оконные зонды TCP с полезной нагрузкой 0 байт - PullRequest
15 голосов
/ 29 января 2011

Я экспериментирую с приложением Android, которое транслирует музыку через UPnP на XBox. Потоковая передача работает по большей части, но довольно часто, через одну или две минуты, потоковая передача останавливается, особенно когда в сети есть другие действия. Это никогда не происходит при потоковой передаче на другие устройства, кроме XBox. Я подтвердил это поведение рядом различных серверных приложений UPnP.

После анализа большого количества следов Wireshark я нашел основную причину. Кажется, что после того, как окно приемника TCP заполнено на XBox, оно только явно объявляет об обновлении окна в ответ на нулевые оконные зонды, которые содержат 1 байт данных полезной нагрузки.

В то время как машины под управлением Windows отправляют зонды Zero Window, содержащие 1-байтовую полезную нагрузку, машины под управлением Linux отправляют зонды, которые содержат 0-байтовые полезные данные (чистые ACK).

В идеальных условиях сети это не проблема, поскольку получатель всегда отправляет одно сообщение ACK обновления окна, как только освободит достаточно места в своем окне, чтобы избежать синдрома глупого окна. Однако, если этот единственный пакет обновления окна пропущен, он никогда больше не будет отвечать на Android-устройства на основе linux, поскольку стек TCP на этих устройствах использует нулевые оконные зонды с полезной нагрузкой 0 байт (они выглядят как пакеты Keep Alive для Wirehsark). ).

TCP-стойка между XBox и WMP выглядит так:

<code>
   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.

Обычная стойка TCP между XBox и клиентом Android выглядит следующим образом:

<code>
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.

Стойло TCP между XBox и моим устройством Android выглядит следующим образом, если пропущено первоначальное объявление об обновлении окна:

<code>
 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 даже не требуются. Требуется только один байт данных.

<code>
   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, тем не менее, возвращается к жизни и снова начинает потоковую передачу.

  • Есть ли способ получить возможности CAP_NET_RAW в приложении для Android, не требуя рутирования устройства?
  • Есть ли какой-нибудь другой прием, который я могу использовать, чтобы заставить уровень TCP Linux отправлять свои датчики нулевого окна с 1 байтом данных полезной нагрузки?
  • Есть ли какая-нибудь другая неясная опция TCP, которую я мог бы попробовать, которая позволила бы мне пробудить стек TCP XBox?
  • Есть ли какой-то другой внеполосный подход, чтобы убедить XBox отправить еще одно обновление Windows?
  • Есть ли какой-то другой совершенно не связанный подход, который я мог бы рассмотреть?

Редактировать: Это описание того, почему предоставленные предложения не будут работать.

  1. TCP_NODELAY влияет только на то, как пакеты отправляются, когда окно открыто. В частности, установка этой опции предотвращает ожидание стеком TCP нескольких мс для получения дополнительных данных в попытке создать пакет TCP, который заполняет MSS. Он не позволяет отправлять данные при закрытии окна получателя.

  2. TCP_QUICKACK влияет на способ приема пакетов ACK хоста. Проблема, с которой я сталкиваюсь, заключается в том, что мне нужно изменить способ подтверждения отправителем ACK пакетов , которые он получает.

  3. MSG_OOB только устанавливает срочный флаг TCP.Срочные данные не обрабатываются по-разному в том, что касается окон, и все равно не будут отправляться при закрытии окна получателя.

  4. Изменение алгоритма управления перегрузкой TCP также не поможет,Поскольку XBox принудительно ограничивает скорость передачи данных до скорости воспроизведения MP3, практически невозможно избежать заполнения окна перегрузки.Может быть возможно уменьшить окно перегрузки, выводя пропускную способность, но это только уменьшит вероятность заполнения окна перегрузки, но не предотвратит его полностью.

  5. Использование UDP не вариант, поскольку использование стека UPnP является обязательным, а UPnP доставляет данные через HTTP, и, следовательно, TCP.

Ответы [ 3 ]

3 голосов
/ 30 января 2011

Я нашел несколько вещей, которые могут помочь:

  1. TCP ioctl(2) TCP_NODELAY заставит ядро ​​немедленно отправить пакет PSH.Это может отсоединить соединение.

  2. TCP ioctl(2) TCP_QUICKACK сделает что-то смешное с ACK-пакетами.Это может разорвать соединение.

  3. Если вы используете send(2), вы можете установить флаг MSG_OOB, который может ткнуть XBox прямо в глаза, привлечь его внимание и, возможно, вещиможет начать все сначала.CISCO написал хорошее резюме того, как разные платформы реагируют на TCP URG , и их совет состоит в том, чтобы избегать использования URG, но это достаточно безумно, это может просто сработать.

  4. Опция сокета TCP TCP_CONGESTION позволяет выбирать различные алгоритмы предотвращения перегрузки.Может быть, вы могли бы найти тот, который помогает избежать заполненных окон в первую очередь?(По крайней мере, TCP Vegas реализован как модуль, возможно, не удастся отказаться от алгоритма предотвращения перегрузки по умолчанию на платформе Android.)

2 голосов
/ 14 января 2013

На самом деле вы столкнулись с ошибкой в ​​Linux. Linux не соответствует RFC793 при работе с ситуациями с нулевым окном. Windows на самом деле делает правильные вещи. Обратите внимание, что RFC 793 НЕ требует, чтобы получатель отправлял незапрошенное сообщение об обновлении окна. Вместо этого требование заключается в том, чтобы отправитель отправлял оконный зонд по крайней мере с ОДНЫМ октетом данных.

0 голосов
/ 30 января 2011

Вы можете рассмотреть возможность использования UDP вместо TCP.Я предполагаю, что вы хотите, чтобы Xbox воспроизводил аудио, а не создавал его копию локально?В этом случае вам действительно все равно, если вы получите каждый пакет надежно.Надежность передачи пакетов - это издержки, которые вы получаете с TCP, но, возможно, вам это не нужно.UDP намного проще и более типичен для потоковой передачи.

...