iperf是什么
Iperf 是一个网络性能测试工具,是跨平台的,支持Window,linux,android平台的运行和测试,可以为任何网络生成标准化的性能测量。
Iperf 可以测试最大 TCP 和 UDP 带宽性能,具有多种参数和UDP特性,可以根据需要调整,可以报告带宽、延迟抖动和数据包丢失。
下载可能会很慢,不行去 https://iperf.fr/iperf-download.php 下载 linux 版本源码
移植步骤
1、解压 iperf 源码,放置到android源码目录下,这里以 system/ 目录为例,你也可以放到 package/app 或者其它目录
2、删除源码目录下的 Makefile.am 和 Makefile.in 文件 以及 src 目录下 Makefile.am 、Makefile.in、Makefile.old
3、新增 Android.mk, 加入如下配置
LOCAL_PATH := $(call my-dir) # Device executable. # ========================================================= include $(CLEAR_VARS) LOCAL_MODULE := iperf LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) LOCAL_C_INCLUDES := $(LOCAL_PATH)/src LOCAL_SRC_FILES := $(call all-c-files-under, src) LOCAL_CFLAGS := -Wno-error -DIPERF_HOST=0 LOCAL_MODULE_TAGS := debug LOCAL_FORCE_STATIC_EXECUTABLE := true # LOCAL_LDLIBS += -llog LOCAL_LDLIBS += -L$(SYSROOT)/usr/lib -llog LOCAL_STATIC_LIBRARIES := libutils liblog include $(BUILD_EXECUTABLE)
遇到的坑
1、config.h 文件找不到,源码中的文件是 config.h.in, 去掉后缀 .in 即可
system/iperf/src/iperf_util.c:29:10: fatal error: 'config.h' file not found #include "config.h" ^~~~~~~~~~ 1 error generated. [ 42% 11/26] target C: iperf <= system/iperf/src/iperf_tcp.c
2、结构体 in6_flowlabel_req 重复定义,解决办法 flowlabel.h 中结构体外增加条件编译控制
In file included from system/iperf/src/iperf_tcp.c:29: system/iperf/src/flowlabel.h:22:8: error: redefinition of 'in6_flowlabel_req' struct in6_flowlabel_req ^ bionic/libc/kernel/uapi/linux/in6.h:55:8: note: previous definition is here struct in6_flowlabel_req { ^ 1 error generated. */ #if IPERF_HOST struct in6_flowlabel_req { struct in6_addr flr_dst; __u32 flr_label; __u8 flr_action; __u8 flr_share; __u16 flr_flags; __u16 flr_expires; __u16 flr_linger; __u32 __flr_pad; /* Options in format of IPV6_PKTOPTIONS */ }; #endif
3、main 函数冲突错误,删除 src/t_uuid.c、src/t_timer.c 和 src/t_units.c 三个文件
prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.9/aarch64-linux-android/bin/ld.gold: error: out/target/product/k37tv1_64_bsp/obj/EXECUTABLES/iperf_intermediates/src/t_timer.o: multiple definition of 'main' prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.9/aarch64-linux-android/bin/ld.gold: out/target/product/k37tv1_64_bsp/obj/EXECUTABLES/iperf_intermediates/src/main.o: previous definition here prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.9/aarch64-linux-android/bin/ld.gold: error: out/target/product/k37tv1_64_bsp/obj/EXECUTABLES/iperf_intermediates/src/t_units.o: multiple definition of 'main' prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.9/aarch64-linux-android/bin/ld.gold: out/target/product/k37tv1_64_bsp/obj/EXECUTABLES/iperf_intermediates/src/main.o: previous definition here prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.9/aarch64-linux-android/bin/ld.gold: error: out/target/product/k37tv1_64_bsp/obj/EXECUTABLES/iperf_intermediates/src/t_uuid.o: multiple definition of 'main' prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.9/aarch64-linux-android/bin/ld.gold: out/target/product/k37tv1_64_bsp/obj/EXECUTABLES/iperf_intermediates/src/main.o: previous definition here clang.real: error: linker command failed with exit code 1 (use -v to see invocation) ninja: build stopped: subcommand failed. 16:16:14 ninja failed with: exit status 1
4、然后再编译,你就阔仪得到 iperf 文件了,输出路径位于 system/xbin/ 下
[ 83% 5/6] target Strip (mini debug info): iperf (out/target/product/k37tv1_64_bsp/obj/EXECUTABLES/iperf_intermediates/iperf) prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.9/bin/aarch64-linux-android-nm: out/target/product/k37tv1_64_bsp/symbols/system/xbin/iperf: no symbols [100% 6/6] Install: out/target/product/k37tv1_64_bsp/system/xbin/iperf #### build completed successfully (44 seconds) ####
5、把 system/xbin/iperf文件 push 到设备中,adb shell 执行命令
先给 iperf 赋权, chmod 777 system/xbin/iperf,然后开启服务端,再执行
iperf -c 192.168.40.37 -p 5201 -t 10
Connecting to host 192.168.40.37, port 5201
iperf3: error - unable to create a new stream: No such file or directory
提示错误,这是因为 iperf 执行时需要指定缓存目录,源码中用的是 char template[] = “/tmp/iperf3.XXXXXX”;
O 以上已经没有 tmp 目录了,将其修改为 char template[] = “/data/local/tmp/iperf3.XXXXXX”;
重新编译push
这样就能正常使用 iperf 工具了
Connecting to host 192.168.40.37, port 5201 [ 4] local 192.168.3.4 port 34986 connected to 192.168.40.37 port 5201 [ ID] Interval Transfer Bandwidth Retr Cwnd [ 4] 0.00-1.07 sec 2.12 MBytes 16.6 Mbits/sec 73 37.2 KBytes [ 4] 1.07-2.11 sec 1.00 MBytes 8.11 Mbits/sec 0 44.1 KBytes [ 4] 2.11-3.17 sec 1.00 MBytes 7.87 Mbits/sec 1 40.0 KBytes [ 4] 3.17-4.10 sec 1.25 MBytes 11.3 Mbits/sec 0 46.9 KBytes [ 4] 4.10-5.00 sec 1.88 MBytes 17.5 Mbits/sec 0 52.4 KBytes [ 4] 5.00-6.00 sec 1.62 MBytes 13.6 Mbits/sec 5 45.5 KBytes [ 4] 6.00-7.10 sec 1.75 MBytes 13.4 Mbits/sec 2 41.4 KBytes [ 4] 7.10-8.20 sec 1.12 MBytes 8.58 Mbits/sec 0 46.9 KBytes [ 4] 8.20-9.20 sec 512 KBytes 4.19 Mbits/sec 0 48.3 KBytes [ 4] 9.20-10.10 sec 640 KBytes 5.82 Mbits/sec 3 35.9 KBytes - - - - - - - - - - - - - - - - - - - - - - - - - [ ID] Interval Transfer Bandwidth Retr [ 4] 0.00-10.10 sec 12.9 MBytes 10.7 Mbits/sec 84 sender [ 4] 0.00-10.10 sec 12.7 MBytes 10.5 Mbits/sec receiver iperf Done.
app 中调用 iperf
每次都需要在 adb shell 下调用测速,其实在 java 代码中也能,通过 Runtime.getRuntime().exec() 执行 iperf 指令
successMsg: Connecting to host 192.168.40.37, port 5201
errorMsg: iperf3: error - unable to create a new stream: Permission denied
在 app 执行时又返回 unable to create a new stream 错误,看来 char template[] 路径还得修改,试了好几个目录后,
最终指定到 sdcard/下解决了问题, char template[] = “/sdcard/iperf3.XXXXXX”;
String cmds = "iperf -c 192.168.40.37 -p 5201 -t 10"; CommandResult result = execCmd(cmds, false, true); String fullResult = result.toString(); public static CommandResult execCmd(final String[] commands, final boolean isRooted, final boolean isNeedResultMsg) { int result = -1; if (commands == null || commands.length == 0) { return new CommandResult(result, "", ""); } Process process = null; BufferedReader successResult = null; BufferedReader errorResult = null; StringBuilder successMsg = null; StringBuilder errorMsg = null; DataOutputStream os = null; try { process = Runtime.getRuntime().exec(isRooted ? "su" : "sh"); os = new DataOutputStream(process.getOutputStream()); for (String command : commands) { if (command == null) { continue; } os.write(command.getBytes()); os.writeBytes(LINE_SEP); os.flush(); } os.writeBytes("exit" + LINE_SEP); os.flush(); result = process.waitFor(); if (isNeedResultMsg) { successMsg = new StringBuilder(); errorMsg = new StringBuilder(); successResult = new BufferedReader( new InputStreamReader(process.getInputStream(), "UTF-8") ); errorResult = new BufferedReader( new InputStreamReader(process.getErrorStream(), "UTF-8") ); String line; if ((line = successResult.readLine()) != null) { successMsg.append(line); while ((line = successResult.readLine()) != null) { successMsg.append(LINE_SEP).append(line); } } if ((line = errorResult.readLine()) != null) { errorMsg.append(line); while ((line = errorResult.readLine()) != null) { errorMsg.append(LINE_SEP).append(line); } } } } catch (Exception e) { e.printStackTrace(); } finally { try { if (os != null) { os.close(); } } catch (IOException e) { e.printStackTrace(); } try { if (successResult != null) { successResult.close(); } } catch (IOException e) { e.printStackTrace(); } try { if (errorResult != null) { errorResult.close(); } } catch (IOException e) { e.printStackTrace(); } if (process != null) { process.destroy(); } } return new CommandResult( result, successMsg == null ? "" : successMsg.toString(), errorMsg == null ? "" : errorMsg.toString() ); }
下载我编译好的 iperf
参考文章
https://www.sdnlab.com/2961.html
https://www.cnblogs.com/0xcafebabe/p/10685938.html
https://blog.csdn.net/Sunxiaolin2016/article/details/100112339