Android 客户端和 Windows 服务器之间的 TCP 连接在随机时间后断开

TCP connection between Android client and Windows server breaks after random time

我在 Java 中为 Android 客户端和 PC 服务器(在我的例子中是 Windows)创建了一个分布式应用程序。 Android 应该永久保持连接以便能够接收推送通知。所以有一个后台服务可以在关闭屏幕后继续存在。 (请不要告诉我使用 GCM。它超出了范围,因为我必须连接到 Internet。)为了测试,客户端每 5 分钟发送一次自制(应用层)ping packet 到服务器,服务器(在延迟后)发送回 pong,在 return 中被确认(ack)。 Server收到ack后,延迟增加25秒

然而,随机地 - 看起来 - 服务器声称客户端不正常地关闭了连接并显示此错误消息:

android java.io.IOException: An existing connection was forcibly closed by the remote host

我注意到有一些因素会延迟(而不是解决!)这个问题:

我确定以下组件没有影响:

如果我运行服务器程序在Linux(Ubuntu)机器上,没有连接失败。

这些连接问题可能是什么原因造成的?如何在不切换到 Linux 的情况下摆脱它们?

我在 OS 版本 4.3 上遇到了同样的问题。我假设您正在处理服务这个片段可能会帮助您保持连接

private PowerManager.WakeLock mWakeLock;
public override void OnCreate ()
{
   PowerManager pm = (PowerManager) GetSystemService(Context.PowerService);
   mWakeLock = pm.NewWakeLock (WakeLockFlags.Partial, "PartialWakeLockTag");
   mWakeLock.Acquire();
}

public override void OnDestroy ()
{
     mWakeLock.Release();
}

显然,让您的设备 CPU 处于活动状态,您将快速消耗电池电量

经过大量研究,我自己想出了解决方案...

这里的主要问题之一是 deep sleep,Android 设备在未被积极使用时进入。我不想使用唤醒锁来减少电池寿命。我没有找到对 Android 的深度睡眠模式的正确描述,但是,在 SystemClock documentation.

中提到了它

不过,深度睡眠对于一般的wifi通信来说不是问题。 Android 以某种方式设法在深度睡眠中接收网络数据包,但不一定唤醒接收应用程序。相反,当应用程序处于活动状态时(例如,由于用户交互或警报事件),它们会被传送到应用程序。

使用 Windows 时会出现问题,因为自 Windows Vista 和 2008 以来,ARP 缓存条目的超时已降低到 15-45 秒 (1.)。这意味着 Windows 大约每 30 秒询问一次 Android 设备的 MAC 地址。这可以使用网络嗅探器来确认。 ("谁有192.168.0.3?告诉192.168.0.2")

如果 Android 正在睡觉,它不会响应。在 30 到 300 秒之间的超时后(我无法更精确地确定它),显然 Windows 声明所有打开的 TPC 连接已断开。 (那是上面的 IOException 来自。)

因此解决方案是为 Android 设备 (2.) or to massively increase the ARP cache timeout (3.) 静态设置 ARP 条目。

我们先试试第二种方法: 以管理员身份打开 cmd 并获取网络接口 ID:

netsh interface ipv4 show interfaces

使用id(此处为11)增加超时基值(默认:30000):

netsh interface ipv4 set interface 11 basereachable=3000000

我也增加了重传间隔(默认:1000)

netsh interface ipv4 set interface 11 retransmittime=10000

输入netsh interface ipv4 show interface 11确认:

Interface Local Area Connection Parameters
----------------------------------------------
IfLuid                             : ethernet_6
IfIndex                            : 11
State                              : connected
Metric                             : 20
Link MTU                           : 1500 bytes
Reachable Time                     : 2850000 ms
Base Reachable Time                : 3000000 ms
Retransmission Interval            : 10000 ms
...
  1. http://blogs.msmvps.com/erikr/2008/09/13/arp-cache-timeout-changed-in-windows-vista-and-2008/
  2. http://www.techrepublic.com/blog/windows-and-office/quick-tips-flush-the-arp-cache-in-windows-7/
  3. http://support.microsoft.com/kb/949589

编辑

虽然增加这些超时值会大大改善这种情况,但仍然会出现同样的问题。所以这变得更加技术化:

Linux下的ARP超时似乎是在/proc/sys/net/ipv4/neigh/eth0/gc_stale_time中指定的,默认为60秒。所以这个值相当于 Windows 默认值。

然而,主要区别在于 ARP 实施:

当Windows需要发送IP包并且该条目的ARP缓存超时时,它广播一个请求“谁有IP?告诉我”。当 Linux 想要这样做时,它使用 最后已知的 MAC 地址 并将其用作请求“谁有 IP?告诉我”的接收者。 (这称为单播轮询并在 RFC 1122 的第 2.3.2.1 (2) 节中定义 --> (1.) )

显然,Android 认为单播请求比广播(地址 FFFFFF:FFFFFF)更重要,并立即回答(因此它从深度睡眠中醒来这样做)。结果:Linux 立即获得 ARP 应答,而 Windows 没有,因此会终止该 MAC 地址的所有 TCP 连接。

  1. https://www.rfc-editor.org/rfc/rfc1122#page-23

EDIT2

我找到了另一种解决方法:

让客户端定期广播它自己的 MAC(比服务器上的 ARP 缓存超时更频繁)。这可以使用 arpping -c 1 -A -I wlan0 192.168.0.4 来完成。这里最大的问题是:arpping 在 Android 上不可用并且 Java 不支持发送 ARP 数据包。由于我不想要本机实现,因此我尝试了上面的第二种方法。

在 Windows 服务器上设置静态路由:

这将找出网络接口名称:netsh interface show interface 例如“本地连接”。然后添加静态ARP路由:netsh interface ip add neighbors "Local Area Connection" "192.168.0.4" "a0-0b-ba-44-f9-3d"

已确认

只是想确认使用如上所述的静态 ARP 路由可以解决问题。没有连接失败。在我的测试中,我至少每 5 分钟向服务器或 Android 客户端发送一条消息。没有数据包丢失。测试时长:10.5小时。