Я экспериментирую с Android-приложением, которое передает музыку через UPnP на XBox. Потоковая передача по большей части работает, но довольно часто через минуту или две потоковая передача останавливается, особенно когда в сети есть другая активность. Этого никогда не происходит при потоковой передаче на другие устройства, отличные от XBox. Я подтвердил такое поведение с помощью ряда различных серверных приложений UPnP.
Проанализировав множество трассировок Wireshark, я нашел основную причину. Кажется, что после того, как окно приемника TCP заполнится на XBox, он только явно повторно объявляет об обновлении окна в ответ на Zero Window Probes, которые содержат 1 байт данных полезной нагрузки.
В то время как машины под управлением Windows отправляют зонды с нулевым окном, содержащие 1-байтовую полезную нагрузку, машины под управлением Linux отправляют зонды, содержащие 0-байтовые полезные данные (чистые ACK).
В идеальных условиях сети это не проблема, поскольку получатель будет всегда посылать сообщение ACK об обновлении одного окна после того, как он освободит достаточно места в своем окне, чтобы избежать синдрома глупого окна. Однако, если этот пакет обновления одного окна будет пропущен, он никогда больше не ответит на устройство Android на базе Linux, потому что стек TCP на этих устройствах использует Zero Window Probes с 0-байтовой полезной нагрузкой (они выглядят как пакеты Keep Alive для Wirehsark ).
Задержка TCP между XBox и WMP выглядит так: Машины на базе Linux отправляют зонды, содержащие 0-байтовые полезные данные (чистые ACK).
В идеальных сетевых условиях это не проблема, поскольку получатель всегда отправляет одно сообщение ACK обновления окна как только он освободит достаточно места в окне, чтобы избежать синдрома глупого окна. Однако, если этот пакет обновления одного окна будет пропущен, он никогда больше не ответит на устройство Android на базе Linux, потому что стек TCP на этих устройствах использует Zero Window Probes с 0-байтовой полезной нагрузкой (они выглядят как пакеты Keep Alive для Wirehsark ).
Задержка TCP между XBox и WMP выглядит так: Машины на базе Linux отправляют зонды, содержащие 0-байтовые полезные данные (чистые ACK).
В идеальных сетевых условиях это не проблема, поскольку получатель всегда отправляет сообщение ACK обновления одного окна как только он освободит достаточно места в окне, чтобы избежать синдрома глупого окна. Однако, если этот пакет обновления одного окна будет пропущен, он никогда больше не ответит на устройство Android на базе Linux, потому что стек TCP на этих устройствах использует Zero Window Probes с 0-байтовой полезной нагрузкой (они выглядят как пакеты Keep Alive для Wirehsark ).
Задержка TCP между XBox и WMP выглядит так: s освободил достаточно места в окне, чтобы избежать синдрома глупого окна. Однако, если этот пакет обновления одного окна будет пропущен, он никогда больше не ответит на устройство Android на базе Linux, потому что стек TCP на этих устройствах использует Zero Window Probes с 0-байтовой полезной нагрузкой (они выглядят как пакеты Keep Alive для Wirehsark ).
Задержка TCP между XBox и WMP выглядит так: s освободил достаточно места в окне, чтобы избежать синдрома глупого окна. Однако, если этот пакет обновления одного окна будет пропущен, он никогда больше не ответит на устройство Android на базе Linux, потому что стек TCP на этих устройствах использует Zero Window Probes с 0-байтовой полезной нагрузкой (они выглядят как пакеты Keep Alive для Wirehsark ).
Задержка TCP между XBox и WMP выглядит так:
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 выглядит следующим образом:
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 выглядит следующим образом:
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 даже не требуются. Единственное, что требуется, - это один байт данных.
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, тем не менее, возвращается к жизни и снова начинает потоковую передачу.
Редактировать: Это описание почему предоставленные предложения не работают.
TCP_NODELAY
влияет только на то, как пакеты отправляются, когда окно открыто. В частности, установка этого параметра предотвращает ожидание стеком TCP в течение нескольких мс дополнительных данных в попытке создать пакет TCP, заполняющий MSS. Это не позволяет отправлять данные, когда окно получателя закрыто.
TCP_QUICKACK
влияет на способ приема пакетов ACK хоста. Проблема, с которой я столкнулся, заключается в том, что мне нужно изменить способ подтверждения отправителем пакетов , которые он получает.
MSG_OOB
устанавливает только флаг срочности TCP. Срочные данные не обрабатываются по-разному в том, что касается окон, и по-прежнему не будут отправлены, когда окно получателя закрыто.
Изменение алгоритма управления перегрузкой TCP тоже не поможет. Поскольку XBox принудительно ограничивает скорость отправки данных скоростью воспроизведения MP3, практически невозможно избежать заполнения окна перегрузки. Можно было бы уменьшить окно перегрузки, сделав вывод о пропускной способности, но это только уменьшило бы вероятность заполнения окна перегрузки, но не предотвратило бы это полностью.
Использование UDP не является вариантом, поскольку использование стека UPnP является обязательным требованием , а UPnP доставляет данные через HTTP и, следовательно, TCP.