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.
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.
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 unimpl
membre à la version actuelle de sdk -
java.net.Socket->impl
est une instance dejava.net.SocketImpl
à la version actuelle de sdk -
java.net.SocketImpl
a unefd
membre de l'actuelle version du sdk -
TCP_KEEPIDLE
,TCP_KEEPINTVL
etTCP_KEEPCNT
ont les mêmes valeurs (4
,5
et6
) à 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 .
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.