如何在Android中设置keepalive超时?

lac*_*ker 7 sockets android tcp keep-alive

我想降低一个Socket的TCP保持活动时间,从2小时开始到10分钟左右.我可以使用socket.setKeepAlive(true)使用keepalive,但是如何控制keepalive数据包发送之前的时间?

看起来我可以这样做,如果我使用NDK,但我想将此代码分发为jar,所以这对我来说并不理想.

and*_*ive 10

我认为能够在每个应用程序级别设置keepalive超时可能非常重要,尤其是在移动设备上,因为它可能在恶劣的网络条件下(wifi /移动).如果应用程序不发送(m)任何数据但使用持久连接,则套接字将不会检测连接是否丢失,除非它发送tcp keepalive探测器.通常可以通过setsockopt(2)调用设置此选项,但android sdk仅提供选项.在堆栈中更深入,该函数调用libcore.io.ForwardingOs.setsockoptInt(...),这是不可用的直接setKeepAlive(boolean),也不是必需的文件描述符.通过使用java反射,无论如何都可以设置keepalive超时,例如:

private final static int SOL_TCP = 6;

private final static int TCP_KEEPIDLE = 4;
private final static int TCP_KEEPINTVL = 5;
private final static int TCP_KEEPCNT = 6;

protected void setKeepaliveSocketOptions(Socket socket, int idleTimeout, int interval, int count) {
  try {
    socket.setKeepAlive(true);
    try {
      Field socketImplField = Class.forName("java.net.Socket").getDeclaredField("impl");
      socketImplField.setAccessible(true);
      if(socketImplField != null) {
        Object plainSocketImpl = socketImplField.get(socket);
        Field fileDescriptorField = Class.forName("java.net.SocketImpl").getDeclaredField("fd");
        if(fileDescriptorField != null) {
          fileDescriptorField.setAccessible(true);
          FileDescriptor fileDescriptor = (FileDescriptor)fileDescriptorField.get(plainSocketImpl);
          Class libCoreClass = Class.forName("libcore.io.Libcore");
          Field osField = libCoreClass.getDeclaredField("os");
          osField.setAccessible(true);
          Object libcoreOs = osField.get(libCoreClass);
          Method setSocketOptsMethod = Class.forName("libcore.io.ForwardingOs").getDeclaredMethod("setsockoptInt", FileDescriptor.class, int.class, int.class, int.class);
          if(setSocketOptsMethod != null) {
            setSocketOptsMethod.invoke(libcoreOs, fileDescriptor, SOL_TCP, TCP_KEEPIDLE, idleTimeout);
            setSocketOptsMethod.invoke(libcoreOs, fileDescriptor, SOL_TCP, TCP_KEEPINTVL, interval);
            setSocketOptsMethod.invoke(libcoreOs, fileDescriptor, SOL_TCP, TCP_KEEPCNT, count);
          }
        }
      }
    }
    catch (Exception reflectionException) {}
  } catch (SocketException e) {}
}
Run Code Online (Sandbox Code Playgroud)

这个工程至少直到以下要求得到满足:

  • libcore.io.ForwardingOs.setsockoptInt/4 存在于当前的sdk版本中
  • java.net.Socketimpl在当前的sdk版本中有一名成员
  • java.net.Socket->impljava.net.SocketImpl当前sdk版本的实例
  • java.net.SocketImplfd在当前的sdk版本中有一个成员
  • TCP_KEEPIDLE,TCP_KEEPINTVLTCP_KEEPCNT具有相同的值(4,56在当前SDK版本)所有的Android装置/硬件架构.

这似乎至少在Android的版本是真的 4.0.1 2011/11月达到最新版本 5.1.1 R9.

luni/src/main/java/libcore/io/Os.java,luni/src/main/java/java/net/Socket.javaluni/src/main/java/java/net/SocketImpl.java平台/ libcore库. TCP_KEEPIDLE,自2.2.3 r2和所有架构以来TCP_KEEPINTVL,TCP_KEEPCNT似乎对Android版本具有相同的值.这可以通过在android platform/ndk存储库中执行来验证.find . -name tcp.h | xargs grep -ho "TCP_KEEP\w\+\s\+\d\+" | sort | uniq -c


Gro*_*Com 1

这可能是一个太明显的答案 [即它不是您的具体情况的选项],但您当然可以通过每 10 分钟发送 1 个丢弃字节(在任一方向)来实现您自己的保持连接。