libcurl 工具类

一、前言
前一篇 【C++开源库】VS2019 编译 libcurl 库 编译除了 libcurl 库,现在来介绍下 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 程序都要使用它。

get 的示例代码如下:

#include <QDebug>
#include <cstring>
#include <curl/curl.h>

// curl 读取到的数据保存到 std::string
size_t curlSaveResponseToStdString(void *contents, size_t size, size_t nmemb, std::string *s) {
    size_t newLength = size * nmemb;
    size_t oldLength = s->size();
    s->resize(oldLength + newLength);
    std::copy((char*)contents, (char*)contents+newLength, s->begin()+oldLength);

    return size * nmemb;
}

int main(int argc, char *argv[]) {
    // 初始化 curl
    CURL *curl = curl_easy_init();

    if (curl) {
        std::string response;
        curl_easy_setopt(curl, CURLOPT_URL, "http://www.qtdebug.com/html/data.json"); // 设置要访问的网址
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curlSaveResponseToStdString); // 告诉 curl 保存响应到 string 中
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response); // 请求的响应保存到变量 response 中
        curl_easy_setopt(curl, CURLOPT_VERBOSE, 0L); // 0 不输出请求的详细信息,1 输出
        CURLcode code = curl_easy_perform(curl);

        if (code == CURLE_OK) {
            // std::cout << response << std::endl; // 中文乱码,因为 std::string 对中文的支持不好
            // qDebug() << QString::fromUtf8(response.data()); // response.data() 返回的是 UTF-8 的字节数据
            qDebug() << QString::fromStdString(response); // 使用 qDebug() 输出,UTF-8 的中文不会乱码
        }
    } else {
        qDebug() << "Error";
    }

    // 释放 curl 资源
    curl_easy_cleanup(curl);

    return 0;
}

至于 curl 的更多使用请参考:C++ 用libcurl库进行http通讯网络编程

下面封装一个 libcurl 工具类。

二、libcurl 工具类

VS 创建个工程,添加 libcurl 的 include 和 lib 路径,再添加 libcurl_a.lib。这里新添加个 libcurl 工具类以封装,暂时只先实现 get 和 post 操作。

CurlUtil.h

#pragma once
#include <string>
#include <iostream>
#include <list>
#include <curl/curl.h>

// libcurl工具类
class CurlUtil
{
public:
	static CurlUtil& Get() {
		static CurlUtil m_curlUtil;
		return m_curlUtil;
	}

	// 执行 HTTP GET 请求
	std::string get(const char* url, CURLcode* code = NULL, std::list<const char*> headers = std::list<const char*>());
	// 执行 HTTP POST 请求
	std::string post(const char* url, const char* data = NULL, bool jsonBody = false, CURLcode* code = NULL, std::list<const char*> headers = std::list<const char*>());

private:
	CurlUtil();

	// curl 读取到的数据保存到 std::string
	static size_t curlSaveResponseToStdString(void* contents, size_t size, size_t nmemb, std::string* s);
};

CurlUtil.cpp

#include "CurlUtil.h"



CurlUtil::CurlUtil()
{
}

// curl 读取到的数据保存到 std::string
size_t CurlUtil::curlSaveResponseToStdString(void* contents, size_t size, size_t nmemb, std::string* s) {
    size_t newLength = size * nmemb;
    size_t oldLength = s->size();
    s->resize(oldLength + newLength);
    std::copy((char*)contents, (char*)contents + newLength, s->begin() + oldLength);

    return size * nmemb;
}

/**
 * @brief 执行 HTTP GET 请求
 * @param url  请求的 URL
 * @param code 请求返回的状态码的指针
 * @param headers 请求头
 * @return 请求执行成功时返回响应的字符串,失败则返回空字符串,请求是否执行成功可以通过 code 进行判断
 */
std::string CurlUtil::get(const char* url, CURLcode* code, std::list<const char*> headers) {
    std::string response;

    // 初始化 curl
    CURL* curl = curl_easy_init();

    if (curl) {
        struct curl_slist* tempHeaders = NULL;

        std::list<const char*>::const_iterator iter;
        for (iter = headers.cbegin(); iter != headers.cend(); ++iter) {
            tempHeaders = curl_slist_append(tempHeaders, *iter);
        }

        curl_easy_setopt(curl, CURLOPT_HTTPHEADER, tempHeaders);
        curl_easy_setopt(curl, CURLOPT_URL, url);
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curlSaveResponseToStdString);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
        curl_easy_setopt(curl, CURLOPT_VERBOSE, 0);

        CURLcode temp = curl_easy_perform(curl);

        if (code != NULL) {
            *code = temp;
        }

        curl_slist_free_all(tempHeaders); /* free the header list */
    }
    else {
        if (code != NULL) {
            *code = CURLE_FAILED_INIT;
        }
    }

    // 释放 curl 资源
    curl_easy_cleanup(curl);

    return response;
}

/**
 * @brief 执行 HTTP POST 请求
 * @param url  请求的 URL
 * @param data 请求的参数
 * @param jsonBody 如果为 true,则请求的参数是 JSON 格式,否则为 Form 表单的格式 key1=value1&key2=value2&...
 * @param code 请求返回的状态码的指针
 * @param headers 请求头
 * @return 请求执行成功时返回响应的字符串,失败则返回空字符串,请求是否执行成功可以通过 code 进行判断
 */
std::string CurlUtil::post(const char* url, const char* data, bool jsonBody, CURLcode* code, std::list<const char*> headers) {
    std::string response;

    // 初始化 curl
    CURL* curl = curl_easy_init();

    if (curl) {
        // Headers
        struct curl_slist* tempHeaders = NULL;

        std::list<const char*>::const_iterator iter;
        for (iter = headers.cbegin(); iter != headers.cend(); ++iter) {
            tempHeaders = curl_slist_append(tempHeaders, *iter);
        }

        if (jsonBody) {
            tempHeaders = curl_slist_append(tempHeaders, "Accept: application/json; charset=utf-8");
            tempHeaders = curl_slist_append(tempHeaders, "Content-Type: application/json");
        }

        curl_easy_setopt(curl, CURLOPT_HTTPHEADER, tempHeaders);
        curl_easy_setopt(curl, CURLOPT_URL, url);
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curlSaveResponseToStdString);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
        curl_easy_setopt(curl, CURLOPT_POST, 1); // POST 请求
        curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data); // POST 参数

        CURLcode temp = curl_easy_perform(curl);

        if (code != NULL) {
            *code = temp;
        }

        curl_slist_free_all(tempHeaders); /* free the header list */
    }
    else {
        if (code != NULL) {
            *code = CURLE_FAILED_INIT;
        }
    }

    // 释放 curl 资源
    curl_easy_cleanup(curl);

    return response;
}

main.cpp

#include "CurlUtil.h"

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

    std::string r1 = CurlUtil::Get().get("http://qtdebug.com/html/data.json");
    std::cout << r1;

    std::cout << "------------------------------------------------";

    std::string r2 = CurlUtil::Get().post("http://eplatform.edu-edu.com.cn/live/api/auth/login", "{ \"username\": \"u1\", \"password\": \"abcd\"}", true);
    std::cout << r2;

    std::cout << "------------------------------------------------";

    CURLcode code;
    std::string r3 = CurlUtil::Get().get("http://eplatform.edu-edu.com.cn/live/api/channels/mine", &code, { "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjU4NjM1Y2Y4NGY0N2M4MGYyNGI1NDQ5NyIsImlhdCI6MTUwOTA3NjY5MCwiZXhwIjoxNTA5MTYzMDkwfQ.6nLBnhjTYJgwjwFf_Lf0LreKryrQ6ITdT-PcGAPhKB8" });
    std::cout << r3;

    return 0;
}

参考:

Qt 使用 curl

上一篇:02 python要点


下一篇:Leetcode easy 237. 删除链表中的节点