diff --git a/clients/android/app/build.gradle b/clients/android/app/build.gradle index 6aeb710..f2caae7 100644 --- a/clients/android/app/build.gradle +++ b/clients/android/app/build.gradle @@ -30,8 +30,8 @@ android { applicationId "su.cin.rapvpn" minSdk 26 targetSdk 35 - versionCode 201 - versionName "0.2.201" + versionCode 202 + versionName "0.2.202" buildConfigField "String", "DEFAULT_BACKEND_URL", "\"${normalizeGradleString(defaultBackendUrl)}\"" buildConfigField "String", "DEFAULT_CLUSTER_ID", "\"${normalizeGradleString(defaultClusterId)}\"" buildConfigField "String", "DEFAULT_ORGANIZATION_ID", "\"${normalizeGradleString(defaultOrganizationId)}\"" diff --git a/clients/android/app/src/main/java/su/cin/rapvpn/VpnPacketWebSocketRelay.java b/clients/android/app/src/main/java/su/cin/rapvpn/VpnPacketWebSocketRelay.java index 799a1c5..5920941 100644 --- a/clients/android/app/src/main/java/su/cin/rapvpn/VpnPacketWebSocketRelay.java +++ b/clients/android/app/src/main/java/su/cin/rapvpn/VpnPacketWebSocketRelay.java @@ -31,6 +31,7 @@ final class VpnPacketWebSocketRelay { private final VpnService vpnService; private final OkHttpClient httpClient; private final FabricServiceChannel fabricServiceChannel; + private final BlockingQueue> priorityIncoming = new ArrayBlockingQueue<>(512); private final BlockingQueue> incoming = new ArrayBlockingQueue<>(2048); private final Object lock = new Object(); @@ -153,7 +154,19 @@ final class VpnPacketWebSocketRelay { connect(clusterId, vpnConnectionId); awaitOpen(Math.min(OPEN_WAIT_MS, Math.max(1, timeoutMs))); int waitMs = Math.max(1, timeoutMs); - List packets = incoming.poll(waitMs, TimeUnit.MILLISECONDS); + List packets = priorityIncoming.poll(); + if (packets != null) { + return packets; + } + packets = incoming.poll(Math.min(2, waitMs), TimeUnit.MILLISECONDS); + if (packets != null) { + return packets; + } + packets = priorityIncoming.poll(); + if (packets != null) { + return packets; + } + packets = incoming.poll(Math.max(1, waitMs - 2), TimeUnit.MILLISECONDS); return packets == null ? new ArrayList<>() : packets; } @@ -167,6 +180,7 @@ final class VpnPacketWebSocketRelay { open = false; connecting = false; connectingSinceMs = 0; + priorityIncoming.clear(); incoming.clear(); if (webSocket != null) { try { @@ -238,10 +252,7 @@ final class VpnPacketWebSocketRelay { if (packets.isEmpty()) { return; } - if (!incoming.offer(packets)) { - incoming.poll(); - incoming.offer(packets); - } + offerIncomingPacketBatch(packets); } @Override @@ -333,6 +344,38 @@ final class VpnPacketWebSocketRelay { return packets; } + private void offerIncomingPacketBatch(List packets) { + BlockingQueue> target = containsTCPControlPacket(packets) ? priorityIncoming : incoming; + if (!target.offer(packets)) { + target.poll(); + target.offer(packets); + } + } + + private static boolean containsTCPControlPacket(List packets) { + if (packets == null) { + return false; + } + for (byte[] packet : packets) { + if (isTCPControlPacket(packet)) { + return true; + } + } + return false; + } + + private static boolean isTCPControlPacket(byte[] packet) { + if (packet == null || packet.length < 20 || (packet[0] >> 4) != 4) { + return false; + } + int ihl = (packet[0] & 0x0f) * 4; + if (ihl < 20 || packet.length < ihl + 20 || packet[9] != 6) { + return false; + } + int flags = packet[ihl + 13] & 0xff; + return (flags & 0x17) != 0; + } + private static String trimRight(String value) { if (value == null) { return "";