Android上的libcurl

Android上的libcurl

curl是一个利用URL语法在命令行方式下工作的文件传输工具。

它支持的协议有:FTP, FTPS, HTTP, HTTPS, GOPHER, TELNET, DICT, FILE 以及 LDAP。

curl同样支持HTTPS认证,HTTP POST方法, HTTP PUT方法, FTP上传, kerberos认证, HTTP上传, 代理服务器, cookies, 用户名/密码认证, 下载文件断点续传, 上载文件断点续传, http代理服务器管道( proxy tunneling), 甚至它还支持IPv6, socks5代理服务器, 通过http代理服务器上传文件到FTP服务器等等,功能十分强大。
4.1 安装与配置
获取代码
git clone https://github.com/curl/curl.git
编译
cd 到curl目录
./buildconf
./configure
make
sudo make install

4.2 curl使用

curl的用法为:

curl [options] [URL…]
,其中options是下载需要的参数,大约有80多个,curl的各个功能完全是依靠这些参数完成的。这里只介绍几种简单的用法,详细的curl的参数在http://curl.haxx.se/docs/说明。

1、读取网页
curl http://www.baidu.com
2、保存网页、下载文件
以page.html命名下载网页:

curl –o page.html http://www.baidu.com
下载某个文件:

curl –O http://101.200.158.37:8980/group1/M00/00/00/ZcieJVfknTiAOScnAAD_2E_cxs4484.png
3、使用cookie来记录session信息
cookie 信息存到cookie1.txt中:

curl –o page.html –D cookie1.txt http://www.linuxidc.com
使用上次的cookie并生成新的cookie:

curl –o page.html –D cookie2.txt -b cookie2.txt
http://www.linuxidc.com
4、断点续传
比如下载一个文件中,突然掉线了,可以这样开始续传:

curl -c -O http://101.200.158.37:8980/group1/M00/00/00/ZcieJVfkjueAe-ctADs41Y5PrD4740.mp3 -o 11.mp3
另外可以用-r选项进行分块下载

5、上传文件
比如我们向ftp传一个文件:

curl -T localfile -u name:passwd ftp://upload_site:port/path/
PS:对于ftp服务器用-u name:passwd选项

7、http提交一个表单GET与POST模式
GET模式什么option都不用,只需要把变量写在url里面就可以了比如:

$curl http://www.linuxidc.com/login.cgi?user=nickwolfe&password=12345
POST模式的选项是 -d

curl -d “user=nickwolfe&password=12345” http://www.linuxidc.com/login.cgi

4.3 libcurl API

libcurl的官方文档在: https://curl.haxx.se/

1、curl编程流程
LibCurl编程流程在基于LibCurl的程序里,主要采用callback function (回调函数)的形式完成传输任务,用户在启动传输前设置好各类参数和回调函数,当满足条件时libcurl将调用用户的回调函数实现特定功能。

下面是利用libcurl完成传输任务流程:

(1)

curl_global_init(); //初始化libcurl

(2)

curl_easy_init(); //函数得到 easy interface型指针
(3)

curl_easy_setopt(); //设置传输选项
(4)

curl_easy_setopt();//设置的传输选项,实现回调函数以完成用户特定任务
(5)

curl_easy_perform();//完成传输任务
(6)

curl_easy_cleanup();//释放内存
在整过过程中设置curl_easy_setopt()参数是最关键的,几乎所有的libcurl程序都要使用它。

2、重要API
(1)初始化全局libcurl
CURLcode curl_global_init(long flags);
描述:

这个函数只能用一次。(其实在调用curl_global_cleanup 函数后仍然可再用)
如果这个函数在curl_easy_init函数调用时还没调用,它讲由libcurl库自动完成。
参数:flags

CURL_GLOBAL_ALL //初始化所有的可能的调用。
CURL_GLOBAL_SSL //初始化支持 安全套接字层。
CURL_GLOBAL_WIN32 //初始化win32套接字库。
CURL_GLOBAL_NOTHING //没有额外的初始化。
(2)释放全局libcurl
void curl_global_cleanup(void);
描述:

在结束libcurl使用的时候,用来对curl_global_init做的工作清理。类似于close的函数。
(3)打印版本号
char *curl_version(void);
描述:

打印当前libcurl库的版本。
(4)得到CURL的指针
CURL *curl_easy_init(void);
描述:

curl_easy_init用来初始化一个CURL的指针(有些像返回FILE类型的指针一样). 相应的在调用结束时要用curl_easy_cleanup函数清理.

一般curl_easy_init意味着一个会话的开始. 它的返回值一般都用在easy系列的函数中.
(5)释放CURL的指针
void curl_easy_cleanup(CURL *handle);
描述:

这个调用用来结束一个会话.与curl_easy_init配合着用.
参数:

CURL类型的指针.
(6)操作curl(重要)
CURLcode curl_easy_setopt(CURL *handle, CURLoption option, parameter);
描述:

这个函数最重要了.
几乎所有的curl 程序都要频繁的使用它.
它告诉curl库.程序将有如何的行为.
比如要查看一个网页的html代码等.
(这个函数有些像ioctl函数)
参数:

CURL *handle // CURL类型的指针
CURLoption option // 各种CURLoption类型的选项.
//(都在curl.h库里有定义,man 也可以查看到)
parameter //这个参数 既可以是个函数的指针,也可以是某个对象的指针,
//也可以是个long型的变量.它用什么这取决于第二个参数.
详细说明:

本节主要介绍curl_easy_setopt中跟http相关的参数。注意本节的阐述都是以libcurl作为主体,其它为客体来阐述的。

option 的主要取值有以下:

  1. CURLOPT_URL

设置访问URL

  1. CURLOPT_WRITEFUNCTION,CURLOPT_WRITEDATA

回调函数原型为:

size_t function( void *ptr, size_t size, size_t nmemb, void *stream);
函数将在libcurl接收到数据后被调用,因此函数多做数据保存的功能,如处理下载文件。

CURLOPT_WRITEDATA 用于表明CURLOPT_WRITEFUNCTION函数中的stream指针的来源。

3.CURLOPT_HEADERFUNCTION,URLOPT_HEADERDATA

回调函数原型为

size_t function( void *ptr, size_t size,size_t nmemb, void *stream);
libcurl一旦接收到http 头部数据后将调用该函数。 CURLOPT_WRITEDATA 传递指针给libcurl,该指针表明CURLOPT_HEADERFUNCTION 函数的stream指针的来源。

  1. CURLOPT_READFUNCTION CURLOPT_READDATA

libCurl需要读取数据传递给远程主机时将调用CURLOPT_READFUNCTION指定的函数,

函数原型是:

size_t function(void *ptr, size_t size, size_t nmemb,void *stream);
CURLOPT_READDATA 表明CURLOPT_READFUNCTION函数原型中的stream指针来源。

5.CURLOPT_NOPROGRESS, CURLOPT_PROGRESSFUNCTION, CURLOPT_PROGRESSDATA

跟数据传输进度相关的参数。

CURLOPT_PROGRESSFUNCTION 指定的函数正常情况下每秒被libcurl调用一次。 为了使CURLOPT_PROGRESSFUNCTION被调用,CURLOPT_NOPROGRESS必须被设置为false。 CURLOPT_PROGRESSDATA指定的参数将作为CURLOPT_PROGRESSFUNCTION指定函数的第一个参数

6.CURLOPT_TIMEOUT,CURLOPT_CONNECTIONTIMEOUT CURLOPT_TIMEOUT 由于设置传输时间,CURLOPT_CONNECTIONTIMEOUT 设置连接等待时间

(7) 提交请求
CURLcode curl_easy_perform(CURL *handle);
描述:

这个函数在初始化CURL类型的指针 以及curl_easy_setopt完成后调用.
就像字面的意思所说perform就像是个舞台.
让我们设置的
option 运作起来.
参数:

CURL类型的指针.
返回值:

返回0意味一切ok,非0代表错误发生。
主要错误码说明:

  1. CURLE_OK
    任务完成一切都好
    2 CURLE_UNSUPPORTED_PROTOCOL
    不支持的协议,由URL的头部指定
    3 CURLE_COULDNT_CONNECT
    不能连接到remote 主机或者代理
    4 CURLE_REMOTE_ACCESS_DENIED
    访问被拒绝
    5 CURLE_HTTP_RETURNED_ERROR
    Http返回错误
    6 CURLE_READ_ERROR
    读本地文件错误

4.4 libcurl 案例

(1) 获取html网页
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <curl/curl.h>

int main(int argc, char *argv[])
{

CURL *curl;     //定义CURL指针
CURLcode res;   //定义CURLcode类型的变量,保存返回状态码

if (argc != 2) {
    printf("Usage: ./a.out <url>\n");
    exit(1);
}
curl = curl_easy_init();

if (curl != NULL) {
             //设置curl选项. 其中CURLOPT_URL是让用户指定url. argv[1]中存放的命令行传进来的网址
            curl_easy_setopt(curl, CURLOPT_URL, argv[1]);

            //调用curl_easy_perform 执行我们的设置.并进行相关的操作. 在这里只在屏幕上显示出来.
            res = curl_easy_perform(curl);
            if (res != CURLE_OK) {
                printf("curl easy perform error\n");
            }

            //清除curl操作.
            curl_easy_cleanup(curl);
}

return 0;

}
gcc -Wall test1_downhtml.c -o test1 -lcurl

./test1 www.baidu.com
(2)、网页下载保存
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>

#include <curl/curl.h>

size_t write_data( void *ptr, size_t size, size_t nmemb, void *stream)
{
FILE fp = (FILE)stream;

int writen = fwrite(ptr, size, nmemb, fp);

return writen;

}

int main(int argc, char *argv[])
{
CURL *curl = NULL;
FILE *fp = NULL;

if (argc != 3) {
    printf("Usage: ./a.out <url> <file>\n");
    exit(1);
}

curl_global_init(CURL_GLOBAL_ALL);


curl = curl_easy_init();

curl_easy_setopt(curl, CURLOPT_URL, argv[1]);

if ((fp = fopen(argv[2], "w")) == NULL) {
    printf("fopen error\n");
    goto END;
}


//CURLOPT_WRITEFUNCTION 将后继的动作交给write_data函数处理
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);

curl_easy_perform(curl);

END:
curl_easy_cleanup(curl);

curl_global_cleanup();


return 0;

}
gcc -Wall test2_download_html.c -o test2 -lcurl
./test2 www.baidu.com baidu.html
(3)、GET请求
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <curl/curl.h>

int main(int argc, char *argv[])
{

CURL *curl = NULL;

curl = curl_easy_init();

curl_easy_setopt(curl, CURLOPT_URL, "127.0.0.1:8080/?username=ldb&passwd=123456");
//将返回的http头 输出到标准输出上
curl_easy_setopt(curl, CURLOPT_HEADERDATA, stdout);
//将返回的get请求数据,输出到标准输出上
curl_easy_setopt(curl, CURLOPT_WRITEDATA, stdout);
curl_easy_perform(curl);

curl_easy_cleanup(curl);

return 0;

}
(4)、POST请求
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <curl/curl.h>

#define POSTURL “http://127.0.0.1:8080/login”
#define POSTFIELDS “{“username”:“liudanbing”, “passwd”:“123456”, “type”:1}”
#define FILENAME “curlpost.log”

size_t write_data(void *buffer, size_t size, size_t nmemb, void *userp)
{
FILE fptr = (FILE)userp;
int ret = 0;
ret = fwrite(buffer, size, nmemb, fptr);

return ret;

}

int main(int argc, char *argv[])
{
CURL *curl;
CURLcode res;
FILE *fptr;

if ((fptr = fopen(FILENAME, "w")) == NULL) {
    fprintf(stderr, "fopen file error: %s\n", FILENAME);
    exit(1);
}

curl = curl_easy_init();
//URL地址
curl_easy_setopt(curl, CURLOPT_URL, POSTURL);
//Post 数据
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, POSTFIELDS);
//对返回的数据进行操作的函数地址
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
//设置WRITEFUNCTION的第四个参数值
curl_easy_setopt(curl, CURLOPT_WRITEDATA, fptr);
//设置为非0表示本次操作为POST
curl_easy_setopt(curl, CURLOPT_POST, 1);
// 设置为非0在执行时打印请求信息
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
//设置为非0将响应头信息同响应体一起传给WRITEFUNCTION
curl_easy_setopt(curl, CURLOPT_HEADER, 1);
//设置为非0,响应头信息Location
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
//设置对应的COOKIEFILE路径
curl_easy_setopt(curl, CURLOPT_COOKIEFILE, "./libcurl.cookie");

res = curl_easy_perform(curl);

if (res != CURLE_OK ) {
    fprintf(stderr, "post perform error\n");
    exit(1);
}
curl_easy_cleanup(curl);

return 0;

}

4.5 Android上使用libcurl

下载和编译
libcurl想要在Android上开发,显然不能只能用C++的so文件。

所以我们需要将curl的源代码通过Android.mk重新编译一次,但是显然很是麻烦。

这里我用就用别人写好的东西。

https://github.com/gcesarmza/curl-android-ios

这个是一个已经写好的移动端使用libcurl的源代码。

下载curl_android_ios

git clone https://github.com/gcesarmza/curl-android-ios.git
由于这个库依赖于 curl库和 openssl,所以需要再次关联下载

git submodule init && git submodule update
编译android版本

/curl-android-ios-master/curl-compile-scripts/

./bulid_android.sh
(1) 问题1

如果提示需要NDK_ROOT环境变量,需要在~/.bashrc加入NDK安装的路径.

vim ~/.bashrc

export NDK_ROOT=/home/itcast/BC-Project/Android-SDK/ndk-bundle

source ~/.bashrc
(2) 问题2

./build_Android.sh: 行 34: ./Configure: 没有那个文件或目录
Error running the ssl configure program
可能是没有下载依赖库openssl 和curl导致。

这个编译可能会话费很长时间,因为他要编译很多种平台的Android。如x86,mips,arm等等。

最后,我们会得到以下静态库文件。

[arm64-v8a] StaticLibrary : libcurl.a
[x86_64] StaticLibrary : libcurl.a
[mips64] StaticLibrary : libcurl.a
[armeabi] StaticLibrary : libcurl.a
[armeabi-v7a] StaticLibrary : libcurl.a
[x86] StaticLibrary : libcurl.a
[mips] StaticLibrary : libcurl.a
对应我们Android的设备的平台型号,选择一款就好了,一般我们都是用的[armeabi]

配置和部署
将生成的libcurl.a 和所对应的头文件拷贝到Android项目中的jni/路径下。

cd ./curl-android-ios-master/prebuilt-with-ssl/android/

cp armeabi/libcurl.a ~/AndroidStudioProjects/testApp/jni/
cp include/curl/ ~/AndroidStudioProjects/testApp/jni/ -R
以上如果您使用的开发环境是Linux。

如果是Windows,需要将armeabi下的libcurl.a通过远程传输,拷贝到当前windows项目中的jni路径下.

测试libcurl
目前我们jni/路径下已经有 libcurl.a 和curl/头文件了。

现在我们可以实现一个login的jni接口,让java调用,来完成客户端和服务端的网络通信。

(1)首先在java类中,定义一个native接口。

//登陆服务器login接口
public native void testLibcurl();

(2) 给该native通过javah生成头文件.

cd testApp/app/src/main/java/

javah -jni com.cpp.itcast.testapp.HelloJni
(3)测试libcurl代码

在jni.cpp中 实现jni接口。 就是访问一个网页,然后将网页的信息打印到debug上。

size_t print_html( void *ptr, size_t size, size_t nmemb, void stream)
{
int len = size
nmemb;
char *buf = new char[len+1];

memcpy(buf, ptr, len);

buf[len] = '\0';

__android_log_print(ANDROID_LOG_ERROR, "jnitag", "%s\n", buf);

delete [] buf;

return len;

}

JNIEXPORT void JNICALL Java_com_cpp_itcast_testapp_HelloJni_testLibcurl
(JNIEnv *, jobject)
{
CURL *curl = NULL;
CURLcode ret;

curl = curl_easy_init();

curl_easy_setopt(curl, CURLOPT_URL, "www.baidu.com");
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, print_html);

ret = curl_easy_perform(curl);
if (ret != CURLE_OK) {
    __android_log_print(ANDROID_LOG_ERROR, "jnitag", "curl_easy_perform error");
    return ;
}

curl_easy_cleanup(curl);

return ;

}
(4)修改Android.mk文件

LOCAL_PATH:=$(call my-dir)

include $(CLEAR_VARS)

libcurl.a

LOCAL_MODULE := libcurl
LOCAL_SRC_FILES := libcurl.a
include $(PREBUILT_STATIC_LIBRARY)

include $(CLEAR_VARS)

#libmyJni.so
LOCAL_MODULE := myJni
LOCAL_SRC_FILES := test.cpp jni.cpp
LOCAL_LDLIBS := -llog -lz
LOCAL_STATIC_LIBRARIES := libcurl
include $(BUILD_SHARED_LIBRARY)
(5) 编译生成libmyJni.so

进入到jni/路径 执行。

ndk-build APP_ABI=“armeabi”
(6)测试 该Jni接口

此过程需要两个必要条件:

1.Android移动设备必须可以上网。

2.给该APP应用开辟上网权限

开辟上网权限如下:

打开testApp/app/src/main/AndroidManifest.xml:

在 标签里面加入如下标签:

<uses-permission android:name="android.permission.INTERNET"/>

3.在app源根文件夹下build.gradle文件的配置

这里面加注释的是额外需要关注的地方。

compileSdkVersion 24
buildToolsVersion "24.0.3"
defaultConfig {
    applicationId "com.example.ace.obo"
    minSdkVersion 10
    targetSdkVersion 24
    versionCode 1
    versionName "1.0"
    testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"


    // 不声明ndk标签,项目默认会创建一个libapp.so的文件
    ndk {
        // 声明创建so库的文件名,会自动添加lib前缀, 添加了前缀,不会自动添加
        moduleName "OBOjni"

        //声明启用Android日志, 在c/c++的源文件中使用的#include <android/log.h> 日志将得到输出
        //这里我们关联了两个库 一个是liblog 和 libz
        ldLibs "log","z"


        // 声明创建指定cpu架构的so库, 不声明的话, 默认(gradle 1.5.0)会生成7中架构,如果你的libcurl没有提供别的平台,那么就会链接失败,
        //所以此条配置很重要,这里我们只生成一个平台
        abiFilters "armeabi"

    }

}

4.另外最好加上如下配置

项目根文件夹下的gradle.properties文件中添加如下配置(解决AS中NDK插件过时不能编译的问题)

android.useDeprecatedNdk=true
5.最后执行测试代码,在真机上运行,观察结果.

 HelloJni.getInstance().testLibcurl();

注意事项:
如果在生成连接到时候 出现 error: linker command failed with exit code 1 (use -v to see invocation)错误

需要过滤生成的ABI平台

### 回答1: Android libcurl.a是一个静态库,它是用C语言编写的libcurl网络传输库的编译结果。静态库是一种在编译时被链接到应用程序中的库,通过将库的对象代码直接嵌入到应用程序中,从而使应用程序可以在没有其他外部依赖项的情况下独立运行。 静态库的使用方式是将其与应用程序进行链接,这样应用程序就可以使用静态库中定义的函数和变量。在Android开发中,可以使用Android Studio等工具libcurl.a添加到项目中,并在项目配置中进行链接设置。 使用Android libcurl.a静态库可以为Android应用程序提供强大而灵活的网络传输功能。libcurl库支持多种协议,包括HTTP、HTTPS、FTP等,并提供了一系列功能来方便地进行网络通信,例如数据传输、下载、上传等。通过使用libcurl库,Android应用程序可以方便地与网络资源进行交互,并实现各种功能,如下载文件、上传数据等。 静态库的一个优点是在编译时能够减少应用程序的体积,因为库的代码嵌入到应用程序中,不需要在运行时再次加载。同时,静态库的使用也可以提高应用程序的性能,因为库的代码可以被编译器优化,以达到更好的执行效率。 总之,Android libcurl.a静态库是一个有用的工具,可以为开发Android应用程序提供强大的网络传输功能,通过与应用程序进行链接,实现各种网络通信需求,同时也能减少应用程序的体积和提升性能。 ### 回答2: Android libcurl.a是一个静态库,它是Curl库在Android平台上的编译结果。Curl库是一个开源的网络传输工具,支持多种协议(包括HTTP、FTP、Telnet等)和一些常见的功能(如文件上传、下载等)。 libcurl.a的静态库形式可以将Curl库的功能和代码整合到一个独立的二进制文件中,这意味着在编译时会将Curl库中的函数和依赖项静态链接到应用程序中。 使用libcurl.a的静态库可以使开发者在编译阶段直接将Curl库的功能包含到应用程序中,不再需要运行时加载动态库文件。这样可以减少应用程序的体积并提升性能,但也意味着更新Curl库需要重新编译应用程序。 要在Android项目中使用libcurl.a,可以将静态库文件复制到项目的jni目录下,然后在JNI层的C/C++代码中引用该库。具体步骤包括在Android.mk中添加libcurl.a的编译指令,并在代码中使用#include导入相关的Curl库头文件。然后可以在应用程序中使用Curl库提供的功能,如发送HTTP请求等。 总之,Android libcurl.a是一个静态库,可以将Curl库的功能整合到Android应用程序中,帮助开发者在应用层面进行网络传输和其他网络相关操作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值