Как установить тайм-аут keepalive в Android?

Я хотел бы снизить время ожидания TCP на сокете, который я открываю от 2 часов до чего-то порядка десяти минут. Я могу использовать keepalive с сокетом.setKeepAlive (true), но как я могу контролировать время до отправки пакета keepalive?

похоже, я мог бы это сделать, если бы использовал NDK, но я хочу распространять этот код как банку, так что это не идеально для меня.

3 ответов


Это, вероятно, слишком очевидный ответ [т. е. это не вариант для вашего конкретного случая], но вы можете, конечно, реализовать свой собственный keepalive, отправив 1 байт выброса (в любом направлении) каждые 10 минут.


я думаю, что это может быть очень важно чтобы иметь возможность установить тайм-ауты keepalive на в приложении уровень, особенно на мобильном устройстве, потому что это может быть в плохих условиях сети (wifi/mobile). Если приложение не отправляет (m) никаких данных но использует постоянное соединение гнездо не обнаружит, потеряно ли соединение, если он не отправляет TCP keepalive зонды. Установка этого опция обычно возможна через setsockopt (2) вызов, но Android sdk предоставляет только . Глубже в стеке, что функции вызывает libcore.Ио.ForwardingOs.setsockoptInt(...), который не может, ни требуемый файловый дескриптор. используя отражение java, установка таймаутов keepalive возможна в любом случае, e.g вот так:

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) {}
}

это работает по крайней мере до следующее требования соблюдены:

  • libcore.io.ForwardingOs.setsockoptInt/4 существует в текущей версии sdk
  • java.net.Socket есть impl участник в текущей версии sdk
  • java.net.Socket->impl - это экземпляр java.net.SocketImpl в текущей версии sdk
  • java.net.SocketImpl есть fd участник в текущей версии sdk
  • TCP_KEEPIDLE, TCP_KEEPINTVL и TCP_KEEPCNT имеют одинаковые значения (4, 5 и 6) в текущая версия SDK и все android устройства / архитектуры.

это, кажется, верно, по крайней мере для android версии С 4.0.1 / ноября до новая версия 5.1.1 r9.

посмотреть luni/src/main/java/libcore/io/Os.java, luni/src/main/java/java/net/Socket.java и luni/src/main/java/java/net/SocketImpl.java С платформа/libcore репозитория. TCP_KEEPIDLE, TCP_KEEPINTVL и TCP_KEEPCNT кажется, есть то же самое значения для версий android с 2.2.3 r2 и всех архитектур. Это можно проверить, например, выполнив find . -name tcp.h | xargs grep -ho "TCP_KEEP\w\+\s\+\d\+" | sort | uniq -c на платформа android / ndk репозитория.


Android основан на Linux, и Linux поддерживает TCP_KEEPIDLE и TCP_KEEPINTVL опции сокета, через setsocketopt() функция, которая обернута java.net.SocketOptions интерфейс. java.net.SocketImpl осуществляет SocketOptions и java.net.Socket обертывает SocketImpl. Я не знаю, можно ли получить доступ к SocketImpl данного