XBox 360 TCP堆栈不响应具有0字节有效负载的TCP零窗口探测器

时间:2011-01-28 23:16:33

标签: android linux networking 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仍然恢复生命,并再次开始流式传输。

  • 有没有办法在Android应用中获取CAP_NET_RAW功能而无需设备根植?
  • 我是否可以使用其他技巧强制Linux TCP层使用1字节的有效负载数据发送其Zero Window Probes?
  • 我可以尝试其他任何模糊的TCP选项,让我唤醒XBox的TCP堆栈吗?
  • 是否有其他一些带外方法来说服XBox发送另一个Window更新?
  • 我可能会考虑其他一些完全不相关的方法吗?

修改:这是对提供的建议无效的说明。

  1. TCP_NODELAY仅影响窗口打开时数据包的发送方式。具体来说,设置此选项可防止TCP堆栈等待几秒钟以获取更多数据,从而尝试创建填满MSS的TCP数据包。当接收器窗口关闭时,它不允许发送数据。

  2. TCP_QUICKACK会影响主机确认其接收数据包的方式。我面临的问题是我需要改变发送方确认正在接收的数据包的方式。

  3. MSG_OOB仅设置TCP紧急标志。就窗口化而言,紧急数据的处理方式不同,当接收者的窗口关闭时,仍然不会发送紧急数据。

  4. 更改TCP拥塞控制算法也无济于事。由于XBox强制将数据发送速率限制为MP3的播放速率,因此几乎不可能避免填充拥塞窗口。可以通过推断吞吐量来减少拥塞窗口,但这只会减少填充拥塞窗口的可能性,而不是完全阻止它。

  5. 使用UDP不是一种选择,因为使用UPnP堆栈是必需的,而UPnP通过HTTP传递数据,因此也就是TCP。

3 个答案:

答案 0 :(得分:3)

我发现了一些可能有用的东西:

  1. TCP ioctl(2) TCP_NODELAY将导致内核立即发送PSH数据包。它可能会破坏连接。

  2. TCP ioctl(2) TCP_QUICKACK将使用ACK数据包做一些有趣的事情。它可能会破坏连接。

  3. 如果您使用send(2),您可以设置MSG_OOB标志,这可能会让XBox正好在眼睛中,引起它的注意,也许事情可能会重新开始。 CISCO写了一个nice summary of how different platforms respond to TCP URG,他们的建议是避免使用URG,但它足够疯狂它可能会起作用。

  4. TCP套接字选项TCP_CONGESTION允许您选择不同的拥塞避免算法。也许你可以找到一个有助于避免填充窗户的地方? (至少TCP Vegas是作为模块实现的,可能无法改变Android平台上的默认拥塞避免算法。)

答案 1 :(得分:2)

实际上,您遇到了Linux错误。在处理零窗口情况时,Linux不符合RFC793。 Windows实际上正在做正确的事情。请注意,RFC 793不要求接收方发送未经请求的窗口更新消息。相反,要求是发送方发送具有至少一个八位字节数据的窗口探测器。

答案 2 :(得分:0)

您可能需要考虑使用UDP而不是TCP。我假设您希望Xbox播放音频,而不是在本地创建它的副本?在这种情况下,您真的不在乎是否可靠地获得每个数据包。数据包传输的可靠性是TCP获得的开销,但也许你真的不需要它。 UDP更简单,在流媒体情况下更为典型。