Jas*_*run 15 linux networking android tcp xbox
我正在尝试通过UPnP将音乐流式传输到XBox的Android应用.流媒体大部分工作,但很快,在一两分钟后,流媒体停滞,特别是当网络上有其他活动时.当流式传输到其他非XBox设备时,这种情况从未发生过.我已经使用许多不同的UPnP服务器应用程序确认了这种行为.
在分析了大量的Wireshark痕迹之后,我找到了根本原因.似乎在TCP接收器窗口填充XBox之后,它只显式重新宣布窗口更新以响应包含1字节有效载荷数据的零窗口探测器.
虽然基于Windows的计算机发送包含1字节有效负载的Zero Window探测器,但基于Linux的计算机会发送包含0字节有效负载(纯ACK)的探测器.
在理想的网络条件下,这不是问题,因为一旦接收器在其窗口中释放足够的空间以避免愚蠢的窗口综合症,它将始终发送单个窗口更新ACK消息.但是,如果错过了单个Window Update数据包,它将永远不会再响应基于Linux的Android设备,因为这些设备上的TCP堆栈使用0字节有效负载的零窗口探测器(它们看起来像Keep Alive数据包到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报文回应.
如果错过了初始的Window Update公告,XBox和我的Android设备之间的TCP停顿会如下所示:
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堆栈等待几秒钟以获取更多数据,从而尝试创建填满MSS的TCP数据包.当接收器窗口关闭时,它不允许发送数据.
TCP_QUICKACK影响主机确认它接收的数据包的方式.我面临的问题是我需要改变发送方确认它接收的数据包的方式.
MSG_OOB仅设置TCP紧急标志.就窗口化而言,紧急数据的处理方式不同,在接收器窗口关闭时仍不会发送.
更改TCP拥塞控制算法也无济于事.由于XBox强制将数据发送速率限制为MP3的播放速率,因此几乎不可能避免填充拥塞窗口.可以通过推断吞吐量来减少拥塞窗口,但这只会减少填充拥塞窗口的可能性,而不是完全阻止它.
使用UDP不是一种选择,因为使用UPnP堆栈是一项要求,UPnP通过HTTP传递数据,因此也就是TCP.
您可能需要考虑使用 UDP 而不是 TCP。我假设您希望 Xbox 播放音频,而不是在本地创建音频副本?在这种情况下,您实际上并不关心是否可靠地获取每个数据包。数据包传输的可靠性是 TCP 带来的开销,但也许您并不真正需要它。UDP 更简单,并且在流媒体情况下更常见。