Files
iperf3-gui/docs/README.md
T
2026-08-25 19:25:40 +09:00

103 lines
4.4 KiB
Markdown

# iperf3-gui
A Flutter-based Android GUI wrapper for iperf3, the popular network performance measurement tool.
## Architecture
```
lib/ # Flutter/Dart application layer
├── main.dart # App entry point (MaterialApp)
├── domain/ # Business logic (platform-independent)
│ ├── models/
│ │ ├── test_configuration.dart # Server, port, duration, interval, parallel streams
│ │ └── iperf3_result.dart # Raw output + exit code
│ └── services/
│ ├── iperf3_service.dart # Dart → MethodChannel bridge (async with Completer)
│ └── iperf3_binary_path_provider.dart # Deprecated (kept for reference)
└── ui/ # Presentation layer
└── features/
└── test_run/
├── test_run_screen.dart # Server/port inputs + GO button
└── test_run_view_model.dart # State machine: idle → running → completed/error
android/app/src/main/
├── kotlin/ # Kotlin plugin layer
│ └── com/novoyuuparosk/iperf3gui/
│ ├── MainActivity.kt # Registers IperfRunner as FlutterPlugin
│ └── IperfRunner.kt # MethodChannel handler → native JNI calls
└── cpp/ # Native iperf3 library
├── CMakeLists.txt # Compiles iperf3 + cJSON + JNI into libiperf3jni.so
├── iperf_jni.c # JNI bridge: Java String[] → iperf3 API
├── iperf/ # iperf3 3.19 C source tree (see below)
├── iperf_config.h # Generated Android-compatible config header
└── iperf_config_android.h # Android-specific compile options
```
## Data Flow
```
User taps GO
→ Dart TestRunViewModel.runTest(config)
→ Iperf3Service.run(config) via MethodChannel
→ Kotlin IperfRunner.runIperfLive(args, callback)
→ JNI C: java_string_array_to_argv() → iperf_parse_arguments()
→ iperf_run_client(test)
→ Reader thread pipes stdout → CallVoidMethod → Dart onOutput callbacks
→ On completion: Dart Completer resolves with Iperf3Result
```
## Key Design Decisions
### JNI Over Process Execution
Android SELinux Enforcing mode blocks `execve()` from the `untrusted_app` domain, making Process.start() of external binaries fail with Permission denied. Compiling iperf3 as a shared library linked into the app process eliminates this restriction entirely — iperf3 code runs in-process with no domain transitions needed.
### Callback Mechanism
The JNI bridge uses `CallVoidMethod` on stored `jmethodID`s to invoke the Kotlin `IperfCallback` interface, NOT function pointer casts. Direct function pointer casts caused SIGBUS crashes on ARM due to JNINativeInterface method ID layout differences.
### Output Streaming
iperf3 output is redirected via pipe to a background reader thread that attaches to the Java VM via `AttachCurrentThread` and invokes callbacks via `CallVoidMethod`. The reader thread frees the state struct on exit; the JNI caller owns the callback object's lifetime.
### Async Pattern
Dart uses `Completer<Iperf3Result>` to await native completion. The MethodChannel callback handler resolves the completer on `onComplete`, with a timeout fallback that calls `stopIperf` on the native layer.
## iperf3 Source
- Version: 3.19
- Location: `android/app/src/main/cpp/iperf/`
- Files: Full C source tree including cJSON, headers, man pages, PEM keys
- Build: CMake compiles into `libiperf3jni.so` for `armeabi-v7a` and `arm64-v8a`
- Config: `iperf_config.h` generated from `iperf_config.h.in` with Android-compatible POSIX/threading macros
## Native JNI API
| Kotlin Method | JNI Signature | Purpose |
|---|---|---|
| `runIperfLive(String[], IperfCallback)` | `runIperfLive` | Start test with args and callback |
| `forceStopIperfTest(IperfCallback)` | `forceStopIperfTest` | Graceful stop via shutdown() |
| `isTestRunning()` | `isTestRunning` | Check if test is in progress |
## Building
Prerequisites:
- Flutter 3.47+
- Android SDK with platform android-36
- JDK 17
- NDK r28+
- CMake 3.22.1
```bash
flutter pub get
flutter build apk --debug
adb install build/app/outputs/flutter-apk/app-debug.apk
```
## Dependencies
- Dart: `provider`, `path_provider`
- Android: CMake external native build
- Native: iperf3 3.19, cJSON (single file)
## License
MIT — bundled iperf3 3.19 is BSD-3-Clause licensed.