iperf3是一个支持TCP, UDP, and SCTP 测带宽的工具。
最近在android上需要测带宽,编译了了一下它。
编译
sh
# auto-detect connected device ABI
./build.sh
# build a specific ABI
./build.sh arm64-v8a
# build arm64-v8a + armeabi-v7a + x86_64 + x86
./build.sh all
# skip adb push
./build.sh --no-push
ubuntu安装使用
sh
sudo apt install iperf3
使用
sh
adb shell /data/local/tmp/iperf3 -s
adb shell /data/local/tmp/iperf3 -c <server-ip>
编译脚本
sh
#!/usr/bin/env bash
# ------------------------------------------------------------------
# build.sh - Cross-compile iperf3 for Android and push to device.
#
# Usage:
# ./build.sh # auto-detect connected device ABI
# ./build.sh arm64-v8a # build a specific ABI
# ./build.sh all # build arm64-v8a + armeabi-v7a + x86_64 + x86
# ./build.sh --no-push # skip adb push
#
# Env overrides:
# ANDROID_NDK_HOME=/path/to/ndk force a specific NDK
# API=24 minSdk (default 21)
# ------------------------------------------------------------------
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$SCRIPT_DIR"
API="${API:-21}"
PUSH_DEST="/data/local/tmp"
JOBS="$(nproc 2>/dev/null || echo 4)"
# ---------- Detect NDK ----------
detect_ndk() {
local d
for v in "${ANDROID_NDK_HOME:-}" "${NDK_HOME:-}" "${ANDROID_NDK:-}"; do
[[ -n "$v" && -d "$v/toolchains/llvm/prebuilt/linux-x86_64" ]] && { echo "$v"; return; }
done
local candidates=()
for d in "$HOME/Android/Sdk/ndk"/* \
"$HOME/Android/Sdk/ndk-bundle" \
/opt/android-ndk-* \
/usr/local/android-ndk-* \
/opt/ndk/* ; do
[[ -d "$d/toolchains/llvm/prebuilt/linux-x86_64/bin" ]] && candidates+=("$d")
done
if [[ ${#candidates[@]} -eq 0 ]]; then
echo "ERROR: Android NDK not found. Set ANDROID_NDK_HOME." >&2
exit 1
fi
# highest version wins
printf '%s\n' "${candidates[@]}" | sort -V | tail -n1
}
NDK="$(detect_ndk)"
TOOLCHAIN="$NDK/toolchains/llvm/prebuilt/linux-x86_64"
echo ">>> NDK: $NDK"
echo ">>> API level: $API"
# ---------- ABI helpers ----------
abi_to_triple() {
case "$1" in
arm64-v8a) echo "aarch64-linux-android" ;;
armeabi-v7a) echo "armv7a-linux-androideabi" ;;
x86_64) echo "x86_64-linux-android" ;;
x86) echo "i686-linux-android" ;;
*) echo "ERROR: unsupported ABI: $1" >&2; exit 1 ;;
esac
}
# The --host value passed to configure (uses 'arm' rather than 'armv7a')
abi_to_host() {
case "$1" in
arm64-v8a) echo "aarch64-linux-android" ;;
armeabi-v7a) echo "arm-linux-androideabi" ;;
x86_64) echo "x86_64-linux-android" ;;
x86) echo "i686-linux-android" ;;
esac
}
# ---------- Detect connected device ABI ----------
detect_device_abi() {
command -v adb >/dev/null 2>&1 || { echo ""; return; }
local n
n=$(adb devices 2>/dev/null | awk 'NR>1 && $2=="device"' | wc -l)
[[ "$n" -eq 0 ]] && { echo ""; return; }
adb shell getprop ro.product.cpu.abi 2>/dev/null | tr -d '\r\n'
}
# ---------- Parse args ----------
ABIS=()
DO_PUSH=1
for arg in "$@"; do
case "$arg" in
all) ABIS+=(arm64-v8a armeabi-v7a x86_64 x86) ;;
--no-push) DO_PUSH=0 ;;
-h|--help) sed -n '2,15p' "$0"; exit 0 ;;
arm64-v8a|armeabi-v7a|x86_64|x86) ABIS+=("$arg") ;;
*) echo "Unknown argument: $arg" >&2; exit 1 ;;
esac
done
if [[ ${#ABIS[@]} -eq 0 ]]; then
dev_abi="$(detect_device_abi)"
if [[ -n "$dev_abi" ]]; then
echo ">>> Detected device ABI: $dev_abi"
ABIS=("$dev_abi")
else
echo ">>> No device connected; defaulting to arm64-v8a"
ABIS=(arm64-v8a)
fi
fi
# ---------- Ensure configure exists ----------
if [[ ! -x ./configure ]]; then
echo ">>> configure not found, running bootstrap.sh"
./bootstrap.sh
fi
# ---------- Build one ABI (out-of-tree) ----------
build_one() {
local ABI="$1"
local TRIPLE HOST CC_BIN BUILD_DIR OUT_DIR
TRIPLE="$(abi_to_triple "$ABI")"
HOST="$(abi_to_host "$ABI")"
CC_BIN="$TOOLCHAIN/bin/${TRIPLE}${API}-clang"
BUILD_DIR="build/$ABI"
OUT_DIR="out/$ABI"
echo
echo "=================================================="
echo ">>> Building iperf3 for $ABI"
echo ">>> triple: $TRIPLE host: $HOST"
echo ">>> CC: $CC_BIN"
echo "=================================================="
[[ -x "$CC_BIN" ]] || { echo "ERROR: compiler missing: $CC_BIN" >&2; exit 1; }
rm -rf "$BUILD_DIR"
mkdir -p "$BUILD_DIR" "$OUT_DIR"
(
cd "$BUILD_DIR"
CC="$CC_BIN" \
AR="$TOOLCHAIN/bin/llvm-ar" \
RANLIB="$TOOLCHAIN/bin/llvm-ranlib" \
STRIP="$TOOLCHAIN/bin/llvm-strip" \
../../configure \
--host="$HOST" \
--without-openssl \
--disable-shared --enable-static \
CFLAGS="-static -fPIE -O2" \
LDFLAGS="-static -pie"
make -j"$JOBS"
)
"$TOOLCHAIN/bin/llvm-strip" "$BUILD_DIR/src/iperf3"
cp "$BUILD_DIR/src/iperf3" "$OUT_DIR/iperf3"
echo ">>> Output: $OUT_DIR/iperf3"
file "$OUT_DIR/iperf3" 2>/dev/null || true
}
for a in "${ABIS[@]}"; do
build_one "$a"
done
# ---------- Push to device ----------
if [[ "$DO_PUSH" -eq 1 ]]; then
if ! command -v adb >/dev/null 2>&1; then
echo ">>> adb not in PATH; skip push."
exit 0
fi
dev_abi="$(detect_device_abi)"
if [[ -z "$dev_abi" ]]; then
echo ">>> No device connected; skip push."
exit 0
fi
push_bin=""
for a in "${ABIS[@]}"; do
[[ "$a" == "$dev_abi" ]] && push_bin="out/$a/iperf3"
done
# only one ABI built and it doesn't match device -> still push (user asked for it)
if [[ -z "$push_bin" && ${#ABIS[@]} -eq 1 ]]; then
push_bin="out/${ABIS[0]}/iperf3"
echo ">>> Warning: built ABI (${ABIS[0]}) != device ABI ($dev_abi); pushing anyway."
fi
if [[ -z "$push_bin" || ! -f "$push_bin" ]]; then
echo ">>> No matching build for device ABI $dev_abi; skip push."
exit 0
fi
echo
echo ">>> Pushing $push_bin -> $PUSH_DEST/iperf3"
adb push "$push_bin" "$PUSH_DEST/iperf3"
adb shell chmod 755 "$PUSH_DEST/iperf3"
echo
echo ">>> Version check on device:"
adb shell "$PUSH_DEST/iperf3" -v || true
echo
echo ">>> Run on device with e.g.:"
echo " adb shell $PUSH_DEST/iperf3 -s"
echo " adb shell $PUSH_DEST/iperf3 -c <server-ip>"
fi
echo ">>> Done."