diff --git a/clients/android/app/build.gradle b/clients/android/app/build.gradle index e04743b..f794079 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 207 - versionName "0.2.207" + versionCode 208 + versionName "0.2.208" 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/RapDiagnosticService.java b/clients/android/app/src/main/java/su/cin/rapvpn/RapDiagnosticService.java index ea3e0c2..289404f 100644 --- a/clients/android/app/src/main/java/su/cin/rapvpn/RapDiagnosticService.java +++ b/clients/android/app/src/main/java/su/cin/rapvpn/RapDiagnosticService.java @@ -21,6 +21,7 @@ import android.os.Looper; import android.provider.Settings; import android.widget.Toast; +import org.json.JSONArray; import org.json.JSONObject; import java.net.DatagramPacket; @@ -472,6 +473,8 @@ public class RapDiagnosticService extends Service { result = runVPNDeepTest(client, clusterId, params); } else if ("vpn_download_test".equals(type)) { result = runVPNDownloadTest(params.optString("url", "http://192.168.200.61:18080/downloads/rap-android-rdp-vpn-build.json")); + } else if ("vpn_mixed_load_test".equals(type) || "vpn_parallel_http_get".equals(type)) { + result = runVPNMixedLoadTest(params); } else if ("launch_telegram".equals(type)) { result = openExternalURL(params.optString("url", "tg://resolve?domain=telegram")); } else if ("remote_assist_start".equals(type)) { @@ -1222,6 +1225,92 @@ public class RapDiagnosticService extends Service { } } + private String runVPNMixedLoadTest(JSONObject payload) { + int parallel = payload.optInt("parallel", 12); + if (parallel < 1) { + parallel = 1; + } + if (parallel > 20) { + parallel = 20; + } + int timeoutMs = payload.optInt("timeout_ms", 20000); + if (timeoutMs < 3000) { + timeoutMs = 3000; + } + if (timeoutMs > 45000) { + timeoutMs = 45000; + } + String rdpHost = payload.optString("rdp_host", "192.168.200.95"); + int rdpPort = payload.optInt("rdp_port", 3389); + String[] defaults = new String[] { + "http://2ip.ru/", + "http://example.com/", + "http://neverssl.com/", + "http://192.168.200.61:18080/downloads/rap-android-rdp-vpn-build.json", + "http://192.168.200.61:18080/" + }; + List urls = new ArrayList<>(); + JSONArray payloadUrls = payload.optJSONArray("urls"); + if (payloadUrls != null) { + for (int i = 0; i < payloadUrls.length(); i++) { + String url = payloadUrls.optString(i, ""); + if (url != null && !url.trim().isEmpty()) { + urls.add(url.trim()); + } + } + } + if (urls.isEmpty()) { + for (String url : defaults) { + urls.add(url); + } + } + String rdpBefore = runVPNTCPConnect(rdpHost, rdpPort, Math.min(timeoutMs, 10000)); + String[] results = new String[parallel]; + Thread[] threads = new Thread[parallel]; + final int requestTimeoutMs = timeoutMs; + long started = System.currentTimeMillis(); + for (int i = 0; i < parallel; i++) { + final int index = i; + final String target = urls.get(i % urls.size()); + threads[i] = new Thread(() -> results[index] = runVPNHttpGet(target, requestTimeoutMs), "rap-vpn-load-test-" + index); + threads[i].start(); + } + for (Thread thread : threads) { + try { + thread.join(timeoutMs + 5000L); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + break; + } + } + long elapsed = System.currentTimeMillis() - started; + int ok = 0; + int failed = 0; + StringBuilder sample = new StringBuilder(); + for (int i = 0; i < results.length; i++) { + String item = results[i]; + if (item != null && item.contains("-> HTTP ")) { + ok++; + } else { + failed++; + } + if (i < 5) { + if (sample.length() > 0) { + sample.append("; "); + } + sample.append(item == null ? "timeout/no_result" : item); + } + } + String rdpAfter = runVPNTCPConnect(rdpHost, rdpPort, Math.min(timeoutMs, 10000)); + return compact("vpn_mixed_load_test parallel=" + parallel + + " ok=" + ok + + " failed=" + failed + + " elapsed_ms=" + elapsed + + " rdp_before={" + rdpBefore + "}" + + " rdp_after={" + rdpAfter + "}" + + " sample={" + sample + "}", 2500); + } + private String openExternalURL(String target) { try { Intent open = new Intent(Intent.ACTION_VIEW, Uri.parse(target)); diff --git a/clients/android/app/src/main/java/su/cin/rapvpn/RapVpnService.java b/clients/android/app/src/main/java/su/cin/rapvpn/RapVpnService.java index 3ee8da0..27426d0 100644 --- a/clients/android/app/src/main/java/su/cin/rapvpn/RapVpnService.java +++ b/clients/android/app/src/main/java/su/cin/rapvpn/RapVpnService.java @@ -56,7 +56,8 @@ public class RapVpnService extends VpnService { private static final boolean PACKET_WEBSOCKET_DATAPLANE_ENABLED = true; private static final int VPN_BATCH_MAX_PACKETS = 512; private static final int VPN_BATCH_MAX_BYTES = 1024 * 1024; - private static final int UPLINK_WORKER_MAX_COUNT = 1; + private static final int UPLINK_WORKER_MIN_COUNT = 4; + private static final int UPLINK_WORKER_MAX_COUNT = 4; private static final int UPLINK_QUEUE_CAPACITY = 32768; private static final int PRIORITY_QUEUE_CAPACITY = 4096; private static final int UPLINK_SEND_RETRY_COUNT = 2; @@ -1028,10 +1029,7 @@ public class RapVpnService extends VpnService { running = true; long runtimeId = runtimeGeneration.incrementAndGet(); runtimeStartedAt = System.currentTimeMillis(); - uplinkWorkerCount = Math.max(1, Math.min(UPLINK_WORKER_MAX_COUNT, Math.max(1, Runtime.getRuntime().availableProcessors() - 1))); - if (uplinkWorkerCount < 2) { - uplinkWorkerCount = 1; - } + uplinkWorkerCount = Math.max(UPLINK_WORKER_MIN_COUNT, Math.min(UPLINK_WORKER_MAX_COUNT, Math.max(1, Runtime.getRuntime().availableProcessors()))); uplinkQueueOffersByWorker = createAtomicCounters(uplinkWorkerCount); uplinkQueueDropsByWorker = createAtomicCounters(uplinkWorkerCount); uplinkSenderPacketsByWorker = createAtomicCounters(uplinkWorkerCount); @@ -2290,7 +2288,8 @@ public class RapVpnService extends VpnService { int ihl = (packet[0] & 0x0f) * 4; int tcpHeaderLength = ((packet[ihl + 12] >> 4) & 0x0f) * 4; int tcpPayloadLength = Math.max(0, ipTotalLength - ihl - tcpHeaderLength); - return syn || fin || rst || (ack && tcpPayloadLength == 0) || (psh && tcpPayloadLength <= 96); + boolean rdp = flow.srcPort == 3389 || flow.dstPort == 3389; + return rdp || syn || fin || rst || (ack && tcpPayloadLength == 0) || (psh && tcpPayloadLength <= 96); } private boolean clampIPv4TCPMSS(byte[] packet, int length, int maxMss) {