Comment configurer le keepalive timeout dans Android?

j'aimerais baisser le temps de conservation TCP sur une prise que j'ouvre de 2 heures à quelque chose de l'ordre de 10 minutes. Je peux utiliser keepalive avec socket.setKeepAlive (true), mais comment puis-je contrôler le temps avant qu'un paquet keepalive ne soit envoyé?

il semble que je pourrais le faire si j'utilisais le NDK, mais je veux distribuer ce code comme un pot, donc ce n'est pas idéal pour moi.

7
demandé sur lacker 2011-07-04 01:26:22

3 réponses

c'est probablement une réponse trop évidente [i.e. ce n'est pas une option pour votre cas spécifique] mais vous pouvez bien sûr mettre en œuvre votre propre keepalive en envoyant 1 byte jeter-away (dans l'une ou l'autre direction) toutes les 10 minutes.

1
répondu GroovyDotCom 2011-07-03 22:57:25

je pense qu'il pourrait être très important pour être en mesure de définir le keepalive timeouts sur un par app niveau, surtout sur un appareil mobile, parce qu'il pourrait être dans de mauvaises conditions réseau (wifi/mobile). Si l'application n'envoie pas (m)de données mais utilise une connexion persistante , la socket ne détectera pas si la connexion est perdue , sauf s'il envoie des sondes tcp keepalive. Le paramétrage de cette option est généralement possible via l'appel setsockopt(2) , mais le sdk android ne fournit que l'option setKeepAlive(boolean) . Plus profondément dans la pile, qui fonctionne appelle libcore.io.ForwardingOs.setsockoptInt ( ... ) , qui est non disponible directement , ni le descripteur de fichier requis. en utilisant java reflection, le réglage des délais keepalive est possible de toute façon , E. g comme ceci:

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

cela fonctionne au moins jusqu'à les exigences suivantes sont satisfaites:

  • libcore.io.ForwardingOs.setsockoptInt/4 existe à la version actuelle de sdk
  • java.net.Socket a un impl membre à la version actuelle de sdk
  • java.net.Socket->impl est une instance de java.net.SocketImpl à la version actuelle de sdk
  • java.net.SocketImpl a une fd membre de l'actuelle version du sdk
  • TCP_KEEPIDLE , TCP_KEEPINTVL et TCP_KEEPCNT ont les mêmes valeurs ( 4 , 5 et 6 ) à la version sdk actuelle et tous les appareils / architectures android.

qui semble être vrai au moins pour les versions android de 4.0.1 / novembre 2011 jusqu'à version récente 5.1.1 r9 .

Voir luni/src/main/java/libcore/io/Os.java , luni/src/main/java/java/net/Socket.java et luni/src/main/java/java/net/SocketImpl.java de la plate-forme/libcore "1519360920 référentiel". TCP_KEEPIDLE , TCP_KEEPINTVL et TCP_KEEPCNT semblent avoir les mêmes valeurs pour les versions android depuis 2.2.3 r2 et tous architecture. Ceci peut être validé par exemple en exécutant find . -name tcp.h | xargs grep -ho "TCP_KEEP\w\+\s\+\d\+" | sort | uniq -c dans le référentiel android platform/ndk .

8
répondu android tcp keepalive 2015-08-11 14:22:49

Android est basé sur Linux, et Linux prend en charge les options TCP_KEEPIDLE et TCP_KEEPINTVL socket via la fonction setsocketopt() , qui est enveloppé par l'interface java.net.SocketOptions . java.net.SocketImpl met en œuvre SocketOptions , et java.net.Socket enveloppe SocketImpl . Ce que je ne sais pas s'il est possible d'accéder à la SocketImpl d'un Socket objet.

ce que vous pourriez essayer de faire est d'utiliser Socket.setSocketImplFactory() pour mettre en œuvre votre propre coutume Classe SocketImplFactory , qui est responsable de la création des instances SocketImpl pour les objets Socket . De cette façon, votre usine pourrait appeler SocketOptions.setOption() pour TCP_KEEPIDLE et TCP_KEEPINTVL pour toutes les sockets que votre application crée.

4
répondu Remy Lebeau 2013-09-04 22:39:25