Я думаю, что весьма важно иметь возможность установить время ожидания активности на уровне для приложения , особенно на мобильном устройстве, потому что ономожет быть в плохих условиях сети (Wi-Fi / мобильный).Если приложение не отправляет (m) никаких данных , но использует постоянное соединение , сокет не будет определять, потеряно ли соединение , если только он не отправляет tcpkeepalive зонды.Установка этой опции обычно возможна с помощью вызова setsockopt (2) , но Android SDK предоставляет только опцию setKeepAlive(boolean)
.Глубже в стеке эта функция вызывает libcore.io.ForwardingOs.setsockoptInt (...) , которая недоступна напрямую , ни требуемый дескриптор файла. Используя 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) {}
}
Это работает по крайней мере до следующие требования выполнены:
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 / ноябрь 2011 до последняя версия 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
из репозитория platform / 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 platform / ndk репозитории.