Android交叉编译环境&Iperf3.0源码编译

在android使用iperf3.0指导文档。
Iperf 是一个网络性能测试工具。

如何在Android手机上使用iperf3.0呢?google(baidu太水了,全是复制粘贴内容,很难查到关键信息,查查八卦还行)查阅了大量的文档,总结了自己编译成功的过程。

1、了解什么是交叉编译:

简单地说,就是在一个平台上生成另一个平台上的可执行代码。

2、Android NDK交叉编译环境准备:

使用的主机:linux
    查看linux的版本【cat /proc/version】
    Linux version 3.13.0-101-generic (buildd@lgw01-40) (gcc version 4.8.4 (Ubuntu 4.8.4-2ubuntu1~14.04.3) ) 
    #148-Ubuntu SMP Thu Oct 20 22:08:32 UTC 2016

    查看系统的位数【uname -a】
    Linux username 3.13.0-101-generic #148-Ubuntu SMP Thu Oct 20 22:08:32 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux

需要使用ndk,在网上下载一个ndk(这个比较简单),我使用的版本
    android-ndk-r14b-linux-x86_64.zip(800M)
    解压到你的自己的目录 /home/my/iperf/android-ndk-linux/android-ndk-r14b

3、搭建交叉编译环境
我们最好使用管理员权限的shell窗口来操作【sudo -s】

a.配置环境变量末尾添加如下三行【vi /etc/profile】
    export NDK=/home/my/iperf/android-ndk-linux/android-ndk-r14b
    export NDK_CROSS=/home/my/iperf/android-ndk-linux/AndroidToolChain/bin
    PATH=$PATH:$NDK:$NDK_CROSS

    注意/AndroidToolChain/bin 这个目录是自己定义的,会自动创建这个目录。后面交叉编译的环境变量会配到这个目录

b.重启环境变量【source /etc/profile】

c.非常重要的一步,执行命令
    $NDK/build/tools/make-standalone-toolchain.sh --platform=android-23 --arch=arm --install-dir=/home/my/iperf/android-ndk-linux/AndroidToolChain/

Android交叉编译环境&Iperf3.0源码编译_第1张图片
运行后的结果
HOST_OS=linux
HOST_EXE=
HOST_ARCH=x86_64
HOST_TAG=linux-x86_64
HOST_NUM_CPUS=4
BUILD_NUM_CPUS=8
Toolchain installed to /home/my/iperf/android-ndk-linux/AndroidToolChain/.

    注意:--platform=android-23指定在哪个android版本。

到了这一步说明交叉编译环境已经配置好了。现在来测试下

4、测试下编译环境是否OK

a.编写一个简单的c程序,test.c
    #include 
    int main()
    {
        printf("hello,from android!\n");
        return 0;
    }

b.把这个test.c文件放到linux你自己的目录下,cd到这个目录,运行命令【arm-linux-androideabi-gcc test.c -o test】
    运行完shell不会有输出,在当前的目录下会生成一个test的执行文件。

c.到这里说明你的交叉编译环境是OK的。还有个小问题
    把test push到手机中
    【adb push test /data/local/tmp】 , 放在这个目录是因为这个目录非root的手也有权限执行命令
    【adb shell】,进入真机
    【cd /data/local/tmp】,cd到这个目录
    【chmod 777 test】,修改文件的权限
    【/data/local/tmp/test】,执行命令

    这个时候会报一个错误 error: only position independent executables (PIE) are supported.

d.解决方法:修改编译命令【arm-linux-androideabi-gcc hello.c -o hello -pie -fPIE】

Android交叉编译环境&Iperf3.0源码编译_第2张图片
加上一个编译的参数

到这里说明你的android交叉编译环境是OK的,下面就来编译Iperf3的源码。

5、下载Iperf3源码

可以在github上去下载,我是在官网上下载的最新版本( iperf-3.3)
下载网址:http://software.es.net/iperf/news.html#security-issue-iperf-3-1-3-iperf-3-0-12-released

Android交叉编译环境&Iperf3.0源码编译_第3张图片

6、将Iperf3.3源码放到linux的你自定义目录上(非常重要)

a.cd到源码的路径下,配置环境变量,如果不配置的话,后面运行就会报错,因为找不到你的交叉编译环境
    【export PATH=/home/my/iperf/android-ndk-linux/AndroidToolChain/bin:$PATH】
    这一步非常重要,没有配置好下面就无法进行下去

Android交叉编译环境&Iperf3.0源码编译_第4张图片

b.配置完成后,运行命令
    【make distclean】我们先clean一下,删除一些之前编译多余的文件。
    【./configure --host=arm-linux CC=arm-linux-androideabi-gcc CXX=arm-linux-androideabi-g++ CFLAGS=-static CXXFLAGS=-static】
    正常情况下这个是OK的,如果有问题再看看log

Android交叉编译环境&Iperf3.0源码编译_第5张图片
注意:实际测试。其实我们这样配置,编译android的可执行Iperf3后面会报一个错误,暂时先不管,把Iperf3编译出来再解决。

7、因为我们是在android上运行,所以需要改点源码。

iperf2直接从其他apk解压得到的执行文件,放在自己的app中可以使用。但是iperf3就不行。查看其他的app里面的iperf3执行文件,发现里面带了app包名路径。
a.改文件 /src/net.c
    #include     -->  #include 

b./src/iperf_api.c
    将这段修改
    if (tempdir == 0){
        tempdir = "/tmp"; /*这个是linux的路径,在android上找不到,需要改成我们包名路径,才会有访问权限*/
    }
    snprintf(template, sizeof(template) / sizeof(char), "%s/iperf3.XXXXXX", tempdir);

    改为:
    if (tempdir == 0){
        tempdir = "/data/data/com.example.test";
    }
    snprintf(template, sizeof(template) / sizeof(char), "%s/iperf3.XXXXXX", tempdir);

c./src/Makefile(注意在./configure ... 命令运行后再改,不然又回自动修改回去)
    将这段
    iperf3_profile_CFLAGS = -pg -g
    iperf3_profile_LDADD = libiperf.la
    iperf3_profile_LDFLAGS = -pg -g

    改为:
    iperf3_profile_CFLAGS = -g
    iperf3_profile_LDADD = libiperf.la
    iperf3_profile_LDFLAGS = -g

    注意:后面实测,其实不该也是可以使用的。

8、源码改好了,下面直接使用【make】命令就ok
我们在Iperf-3.3源码目录下,运行命令make,在/src/目录下就会有Iperf3可执行文件。
Android交叉编译环境&Iperf3.0源码编译_第6张图片

我们可以看下编译出来的android可执行文件的属性
cd到src目录执行【file iperf3】
    输出:iperf3: ELF 32-bit LSB  executable, ARM, EABI5 version 1 (SYSV), dynamically linked (uses shared libs), not stripped

可以看到是arm的可执行文件

9、下面放到android真机上看看是不是OK

测试发现出现一个和上面同样的问题 error: only position independent executables (PIE) are supported.
修改configure参数
在stackoverflow网上找到的
【./configure --host=arm-linux  CC=arm-linux-androideabi-gcc CXX=arm-linux-androideabi-g++ CFLAGS="-static -fPIC" CXXFLAGS="-static" LDFLAGS="-pie -fuse-ld=bfd"】
CFLAGS="-static -fPIC" CXXFLAGS="-static" LDFLAGS="-pie -fuse-ld=bfd"

自己修改为下面也可以成功编译
【./configure --host=arm-linux  CC=arm-linux-androideabi-gcc CXX=arm-linux-androideabi-g++ CFLAGS="-static --pie -fPIE" CXXFLAGS="-static" LDFLAGS="-pie -fPIE"】
CFLAGS="-static --pie -fPIE" CXXFLAGS="-static" LDFLAGS="-pie -fPIE"

重新走一遍上面的流程,可以正常执行。

10、window上运行的iperf3.1版本

手机上使用iperf3.3,运行完成后,发现服务端总是打印一个错误
    iperf3: the client has unexpectedly closed the connection
但是不影响使用。

Android交叉编译环境&Iperf3.0源码编译_第7张图片
解决方法:后面使用iperf3.1版本编译,运行没有发现有这个错误,可能是因为版本不匹配。

11、后面又遇到一个奇葩的问题。

在app中使用Runtime命令执行iperf命令后,在stdout标准流中读不到任何数据。但是其他的iperf 和 ping 都可以
代码:
    process = Runtime.getRuntime().exec("/data/data/com.example.test/iperf3 -c 192.168.1.2");

    buf = new BufferedReader(new InputStreamReader(process.getInputStream(), "utf-8"));
    String str = "";
    final StringBuffer sb = new StringBuffer();

    while ((str = buf.readLine()) != null) {
        sb.append(str);
        LogUtils.d(TAG, "结果:" + str);
    }

发现在buf.readLine()一直阻塞,命令运行完成会一次性发送过来。这样我们就无法实时看到输出了。

解决方法:
    后面查阅大量文档,修改iperf3源码,把输出到stdout的内容重定向到stderr中,读取error中的流。
    a.修改方法,打开iperf_api.c ,搜索关键字 struct iperf_test * ,里面有定义输出的地方
        将这段
            /* By default all output goes to stdout */
            test->outfile = stdout;

            return test;

        修改:
            /* By default all output goes to stdout */
            test->outfile = stderr;

            return test;

    b.app读取error中的流
        process = Runtime.getRuntime().exec("/data/data/com.example.test/iperf3 -c 192.168.1.2");

        buf = new BufferedReader(new InputStreamReader(process.getErrorStream(), "utf-8"));
        String str = "";
        final StringBuffer sb = new StringBuffer();

        while ((str = buf.readLine()) != null ) {
            sb.append(str);
            LogUtils.d(TAG, "结果:" + str);
        }

    测试OK

你可能感兴趣的:(其它)