Я экспериментирую с приложением 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?
- Есть ли какой-то другой совершенно не связанный подход, который я мог бы рассмотреть?
Редактировать: Это описание того, почему предоставленные предложения не будут работать.
TCP_NODELAY
влияет только на то, как пакеты отправляются, когда окно открыто. В частности, установка этой опции предотвращает ожидание стеком TCP нескольких мс для получения дополнительных данных в попытке создать пакет TCP, который заполняет MSS. Он не позволяет отправлять данные при закрытии окна получателя.
TCP_QUICKACK
влияет на способ приема пакетов ACK хоста. Проблема, с которой я сталкиваюсь, заключается в том, что мне нужно изменить способ подтверждения отправителем ACK пакетов , которые он получает.
MSG_OOB
только устанавливает срочный флаг TCP.Срочные данные не обрабатываются по-разному в том, что касается окон, и все равно не будут отправляться при закрытии окна получателя.
Изменение алгоритма управления перегрузкой TCP также не поможет,Поскольку XBox принудительно ограничивает скорость передачи данных до скорости воспроизведения MP3, практически невозможно избежать заполнения окна перегрузки.Может быть возможно уменьшить окно перегрузки, выводя пропускную способность, но это только уменьшит вероятность заполнения окна перегрузки, но не предотвратит его полностью.
Использование UDP не вариант, поскольку использование стека UPnP является обязательным, а UPnP доставляет данные через HTTP, и, следовательно, TCP.