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

时间:2015-01-29 10:43:58

标签: java android tcp

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

然而,随机 - 似乎 - 服务器声称客户端已经非正常地关闭了此错误消息的连接:

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

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

  • 同一无线网络AP上的其他wifi网络活动。
  • 在Android设备上激活并启动Google服务框架。
  • 通过cygwin / ssh设置SSH隧道,并通过此隧道连接到服务器。

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

  • Oracle JDK与openJDK(版本7,32位,Windows)
  • 网卡(试过不同的USB wifi棒以及电缆)
  • Wifi AP(使用DWL-2100AP,Netgear WGT624 v2,FritzBox 6360)
  • 使用Galaxy Nexus(4.3)与使用Galaxy Nexus 4(4.2.2)

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

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

2 个答案:

答案 0 :(得分:0)

我在OS Version 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处于活动状态,您将快速饮用电池汁

答案 1 :(得分:0)

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

此处的一个主要问题是Android设备在未被主动使用时输入的deep sleep。我不想使用唤醒锁来减少电池电量。我没有找到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设备的ARP条目(2.)或大规模增加ARP缓存超时(3.)。

让我们先尝试第二种方法: 以管理员身份打开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

    修改

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

    似乎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则不会因此杀死所有TCP连接。

    1. http://tools.ietf.org/html/rfc1122#page-23

      <强> EDIT2

    2. 我找到了另一个解决方案:

      让客户端定期广播自己的MAC(比服务器上的ARP缓存超时更频繁)。这可以使用arpping -c 1 -A -I wlan0 192.168.0.4完成。这里最大的问题:Android上没有arpping,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"

      <强> CONFIRMED

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