Yo creo que puede ser bastante importante para poder establecer el keepalive tiempos de espera en una por nivel de aplicación, sobre todo en un dispositivo móvil, ya que podría estar bajo condiciones de red malo (wifi/móvil) . Si la aplicación no envía (m) ningún dato pero usa una conexión persistente, el socket no detectará si la conexión se pierde, a menos que envíe tcp sondas keepalive. La configuración de esta opción generalmente es posible a través de la llamada setsockopt(2), pero la sdk de Android proporciona solo la opción setKeepAlive(boolean)
. Más profundo en la pila, que funciona llamadas libcore.io.ForwardingOs.setsockoptInt(...), que es no disponible directamente, ni el descriptor de archivo requerido. Al usar la reflexión de java, es posible establecer los tiempos de espera de keepalive de todos modos, e.g como esto:
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) {}
}
Esto funciona al menos hasta la siguiente se cumplen los requisitos:
libcore.io.ForwardingOs.setsockoptInt/4
existe en la versión actual SDK
java.net.Socket
tiene un miembro de impl
en el versión sdk actual
java.net.Socket->impl
es instancia de java.net.SocketImpl
en la versión actual SDK
java.net.SocketImpl
ha un miembro de fd
en la versión actual SDK
TCP_KEEPIDLE
, TCP_KEEPINTVL
y TCP_KEEPCNT
tienen los mismos valores (4
, 5
y 6
) en la versión actual SDK y todos los dispositivos/arquitecturas de Android.
Eso parece ser cierto al menos para las versiones de Android de4.0.1/November 2011 hasta reciente versión5.1.1 r9.
Ver luni/src/main/java/libcore/io/Os.java
, luni/src/main/java/java/net/Socket.java
y luni/src/main/java/java/net/SocketImpl.java
desde el repositorio platform/libcore. TCP_KEEPIDLE
, TCP_KEEPINTVL
y TCP_KEEPCNT
parecen tener los mismos valores para las versiones de Android desde 2.2.3 r2 y todas las arquitecturas. Esto puede ser validado, p. ejecutando find . -name tcp.h | xargs grep -ho "TCP_KEEP\w\+\s\+\d\+" | sort | uniq -c
en el repositorio android platform/ndk.
Sí, probablemente termine haciendo eso. Sin embargo, he oído que es más exigente con la duración de la batería. Puedo configurar el keepalive del lado del servidor que, con suerte, es razonable. – lacker