libcurl在arm-linux上的应用(交叉编译)

发布时间 2024-01-03 09:25:53作者: 夜星织梦

常见的使用c/c++进行http/https请求的方案

  • libcurl库
    libcurl是一个开源的网络数据传输库,支持http、https、ftp等协议,可以在各种操作系统上使用。
  • libevent库
    libevent是一个事件驱动的网络编程库,可以处理多个网络连接和套接字,支持http、https等协议。
  • boost库
    boost是一个C++库集合,也包含了网络编程库,可以实现http/https请求
  • Qt库
    Qt是一个跨平台的应用程序开发框架,包含了网络编程库,可以实现http/https请求
  • socket编程
    使用socket编程可以手动实现http/https请求,但要自己处理http/https协议的细节

实际应用中使用libcurl库是最常见、最方便的实现http/https请求的方案

libcurl

源码下载

libcurl库在默认情况下已经包含了大部分需要的功能,但是如果需要使用某些特定的功能,可能需要依赖一些第三方库。以下是一些常见的libcurl依赖的第三方库:

第三方库 说明
OpenSSL 用于支持https协议
GnuTLS 用于支持https协议
NSS 用于支持https协议
wolfSSL 用于支持https协议
mbed TLS 用于支持https协议
zlib 用于支持HTTP响应数据的压缩和解压缩
OpenLDAP 用于支持轻量目录访问协议
heimdal 提供对GSS-API的支持,用于在libcurl中提供KerberosSPNEGO身份验证
MIT Kerberos 提供对GSS-API的支持,用于在libcurl中提供KerberosSPNEGO身份验证
nghttp2 用于支持HTTP/2协议
c-ares 用于支持异步DNS解析,可以提高网络请求的性能
libidn2 用于支持国际化域名(IDN),如.CN、.TW、.ORG等域名
libssh2 用于支持SSH协议,可以用于SFTP、SCP等文件传输协议

不同版本的libcurl库可能会依赖不同的第三方库,具体依赖关系可以查看对应版本的文档。在编译和安装libcurl库时,需要确保依赖的第三方库已经安装,并且需要使用相应的编译选项来启用对应的功能
此处使用Openssl和zlib与libcurl进行交叉编译,libcurl默认为最新版

libcurl下载github官网
openssl下载官网根据编译工具链选择合适的版本
zlib下载官网

编译

zlib编译

版本:1.2.12
该版本的configure不支持设置--host项,因此需要手动更改Makefile,打开Makefile文件,将其中的CC、AR、RANLIB都修改为arm-linux交叉编译器的相关参数
image
操作顺序

./configure --prefix=安装目录
修改Makefile
make
make install

openssl编译

版本:OpenSSL 1.1.x
操作顺序

./Configure \
CROSS_COMPILE=/opt/gccenv/arm4.5.1/bin/arm-none-linux-gnueabi- \
--prefix=/home/dcb/code/log/3rdParty/openssl/output \
linux-armv4 no-asm shared
make
make install

libcurl编译

版本:官网最新
操作顺序

./configure --prefix=/home/dcb/code/log/3rdParty/curl/output  \
--host=arm-none-linux-gnueabi \
CC=/opt/gccenv/arm4.5.1/bin/arm-none-linux-gnueabi-gcc \
--with-openssl=/home/dcb/code/log/3rdParty/openssl/output/ \
--with-zlib=/home/dcb/code/log/3rdParty/zlib/output/ \
--enable-shared=1
make
make install

证书生成

除了在编译libcurl时添加对https的支持,还需要配置证书,以下为生成自签名证书相关

openssl req -x509 -newkey rsa:4096 -keyout mycert.pem -out mycert.pem -days 365 -nodes

应用

将编译的openssl,zlib和libcurl头文件和库文件放到对应的工程中image

main.cpp

#include <iostream>
#include <sstream>
#include <string.h>
#include "include/curl/curl.h"

using namespace std;

size_t ReceiveData(void *buffer, size_t size, size_t nmemb, void *stream);
string PostToHost(const char *url, const char *data, CURLcode *res);

int main()
{
    string url = "https://www.cnblogs.com/";
    string data = "";
    string reply;
    CURLcode code;
    reply = PostToHost(url.c_str(), data.c_str(), &code);
    cout << "CURLcode: " << code <<  endl;
    cout << reply << endl;
    return 0;
}

size_t ReceiveData(void *buffer, size_t size, size_t nmemb, void *stream)
{
    string data((const char *)buffer, (size_t)size * nmemb);
    *((stringstream *)stream) << data << endl;
    return size * nmemb;
}

string PostToHost(const char *url, const char *data, CURLcode *res)
{
    CURL *curl;
    char len[32] = {0};
    struct curl_slist *headers = NULL;
    curl = curl_easy_init();
    if (!curl) {
        *res = CURLE_FAILED_INIT;
        cout << __FUNCTION__ << " " << __LINE__ << " CURL INIT FALIED" << endl;
        return "";
    }

    std::stringstream out;
    headers = curl_slist_append(headers, "Accept: application/json");
    snprintf(len, sizeof(len), "Content-Length:%d", (int)strlen(data));
    headers = curl_slist_append(headers, len);
    headers = curl_slist_append(headers, "charset: UTF-8");
    headers = curl_slist_append(headers, "Expect:");
    struct curl_slist *p = headers;
    while(p->next != NULL){
        printf("headers: %s\n",p->data);
        p = p->next;
    }
    // 设置请求体(post) get请求可以不用设置这个参数
    curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data);
    // 输出详细信息用户调试
    curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
    // 设置请求url
    curl_easy_setopt(curl, CURLOPT_URL, url);
    // 设置请求头
    curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
    // 设置回调函数,当http服务器有数据返回时调用这个回调函数
    // 回调函数必须以下面的函数为原型
    // size_t function( char *ptr, size_t size, size_t nmemb, void *userdata);
    // 被调用ptr指向的数据由size*nmemb(乘积)获得
    // 若不设置此回调函数,默认将接收的数据返回到终端
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, ReceiveData);
    // 指定第三个参数作为“CURLOPT_WRITEFUNCTION” 回调函数的第四个参数
    // 若不使用回调函数保存数据,则此部分的第三个参数必须为FILE指针,
    // 函数会将接收到的数据自动写入到这个FILE指针指向的文件流中
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, &out);
    // 设置为非零值
    // 否则POST是普通的 application/x-www-from-urlencoded 类型
    curl_easy_setopt(curl, CURLOPT_POST, 1);
    // 设置连接主机的最长等待时间
    curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 10);
    // 设置整个cURL函数执行的时间(包含连接等待时间
    curl_easy_setopt(curl, CURLOPT_TIMEOUT, 20);
    // CURLOPT_HEADER: 若把一个头包含在输出中,则设置为1。
    curl_easy_setopt(curl, CURLOPT_HEADER, 0);
    // 若设置为1,则会跟踪爬去重定向页面
    curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
    // 0:禁止 cURL 验证对等证书
    // 1:验证证书,要验证的证书在CURLOPT_CAINFO 中设置或CURLOPT_CAPATH中设置证书目录
    curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
    curl_easy_setopt(curl, CURLOPT_SSLVERSION, 4);
    // 配置 https 请求所需证书
    curl_easy_setopt(curl, CURLOPT_SSLCERT, "./ce/mycert.pem");
    curl_easy_setopt(curl, CURLOPT_SSLCERTTYPE, "PEM");
    curl_easy_setopt(curl, CURLOPT_SSLKEY, "./ce/mycert.pem");
    curl_easy_setopt(curl, CURLOPT_SSLKEYTYPE, "PEM");

    *res = curl_easy_perform(curl);
    curl_slist_free_all(headers);
    curl_easy_cleanup(curl);
    curl_global_cleanup();
    return out.str();
}