libcurl的简单使用

libcurl 是一个优秀的网络请求库,支持多种协议, 多平台, 这里我们就现简述一下http协议的使用

准备

本教程是在linux环境下编译和使用的

  • 使用libcurl必须先编译,这个问题的话官方的仓库有简述, 我们就不再赘述,直接上仓库地址
  • https://github.com/curl/curl
  • 导入头文件 #include

步骤

  • 初始化curl
  • 创建easy_handle
  • 设置请求参数
  • 对输出的数据进行回调的数据处理
  • 创建一个容器存储数据
  • 清理curl(全局 和 局部)

范例

#include 
#include 
#include 
#include 
#include 
#include "curl/curl.h"

size_t write_data(void* buffer, size_t size, size_t nmemb, void* userp)
{

    //将void* 指针转换成char* 指针, 并且读取对应长度
    std::string data((char*) buffer, size * nmemb);
    //输出到开发者设置的数据类型中, 这里是stringstream
    *((std::stringstream*) userp) << data << std::endl;

    return  size * nmemb;
}


std::string network(const std::string& url)
{

    //《正题开始, 关注这个部分》

    //创建一个easy_handle, 不要在线程直接共享easy_handle
    CURL* easy_handle = curl_easy_init();

    //数据输出存储的对象
    std::stringstream out;

    //检查是否创建成功
    if(easy_handle == NULL)
    {
        //抛出错误异常
        throw std::runtime_error("create easy_handle fail");
    }

    curl_easy_setopt(easy_handle, CURLOPT_URL, url.c_str());

    //如果不提供这样的回调函数,那个curl只是简单的把数据输出到标准输出流中
    curl_easy_setopt(easy_handle, CURLOPT_WRITEFUNCTION, &write_data);

    //绑定数据输出
    curl_easy_setopt(easy_handle, CURLOPT_WRITEDATA, &out);

    //开始进行请求
    curl_easy_perform(easy_handle);

    //清理curl_easy
    curl_easy_cleanup(easy_handle);

    return out.str();
}

void printRet(std::future& future)
{
    //异常处理
    try {
        //获取请求的结果,为了避免住主线程堵塞, 我们在子线程中等待结果完成
        std::string ret = future.get();
        //输出结果
        std::cout << "curl result:" << ret << std::endl;
    } catch (std::exception& e)
    {
        e.what();
    }
}


int main(int argc, char* args[])
{
    //1.使用curl需要进行全局初始化,支持ssl
    curl_global_init(CURL_GLOBAL_SSL);

    //2.请求地址
    std::string url = "https://www.baidu.com";
    //3.这里我们使用异步来处理网络请求的任务
    std::packaged_task task(network);
    std::future ret = task.get_future();


    //4.将任务移新的线程中去, std::move, std::ref 分别对应右值移动, 和引用绑定
    std::thread t = std::thread(std::move(task), std::ref(url));

    //5.开辟另外一个线程处理数据
    std::thread t2 = std::thread(std::move(printRet), std::ref(ret));


    //TODO:此处做其他事情

    //6.最后我们等待子线程处理任务完成
    t.join();
    t2.join();

    //7.清理全局curl
    curl_global_cleanup();

    return  0;
}

同步请求

#include 
#include 
#include 
#include 
#include "curl/multi.h"

static const char* urls[] = {
        "https://www.baidu.com",
        "https://www.baidu.com",
};


#define MAX 2

size_t write_data(char* buffer , size_t size, size_t nmemb, void* userp)
{
    (void) buffer;
    (void) userp;
    return  size * nmemb;
}

void init(CURLM* cm, int i)
{
    CURL* easy_handle = curl_easy_init();

    const char* url = urls[i];

    curl_easy_setopt(easy_handle, CURLOPT_WRITEFUNCTION, write_data);
    curl_easy_setopt(easy_handle, CURLOPT_URL, urls[i]);
    //保存请求地址
    curl_easy_setopt(easy_handle, CURLOPT_PRIVATE, urls[i]);

    /添加到并发curlm 句柄中
    curl_multi_add_handle(cm, easy_handle);
}

int main()
{
    CURLM* cm;
    CURLMsg* msg;
    int max_fd, msg_left, still_running = -1;
    long curl_timeo;
    //fd_set结构体,本质上是是long类型的数组
    fd_set fd_write, fd_read, fd_except;
    struct timeval T;

    curl_global_init(CURL_GLOBAL_ALL);
    //初始化curlm
    cm = curl_multi_init();

    curl_multi_setopt(cm, CURLMOPT_MAXCONNECTS, (long)MAX);

    for (int i = 0; i < MAX; ++i) {
        init(cm, i);
    }

    do {
        curl_multi_perform(cm, &still_running);
        if(still_running)
        {
            FD_ZERO(&fd_read);
            FD_ZERO(&fd_write);
            FD_ZERO(&fd_except);

            //获取需要监听的文件描述符,分别存放在fd_set 类型的数据中
            if(curl_multi_fdset(cm, &fd_read, &fd_write, &fd_except, &max_fd) != CURLM_OK)
            {
                fprintf(stderr, "curl_multi_fdset");
                return EXIT_FAILURE;
            }

            if(curl_multi_timeout(cm, &curl_timeo) != CURLM_OK)
            {
                fprintf(stderr, "curl_multi_timeout");
                return EXIT_FAILURE;
            }

            if(curl_timeo == -1)
            {
                curl_timeo = 100;
            }

            //如果max_fd 等于就休眠一段时间后,继续执行curl_multi_perform
            if(max_fd == -1)
            {
                std::this_thread::sleep_for(std::chrono::microseconds(100));
            }
            else
            {
                //计算秒
                T.tv_sec = curl_timeo / 1000;
                //计算毫秒
                T.tv_usec = (curl_timeo % 1008) * 1000;

                if (select(max_fd + 1, &fd_read, &fd_read, &fd_except, &T) < 0)
                {
                    fprintf(stderr, "E: select(%i,,,,%li): %i: %s\n",
                            max_fd+1, curl_timeo, errno, std::strerror(errno));
                    return EXIT_FAILURE;
                }
            }
        }

    } while (still_running);

    //IO读写完成,执行读取操作
    while ((msg = curl_multi_info_read(cm, &msg_left)))
    {
        //检测数据是否完整
        if(msg->msg == CURLMSG_DONE)
        {
            char* url;
            //获取easy_handle 句柄
            CURL* easy_handle = msg->easy_handle;
            //使用句柄.获取请求的URL地址
            curl_easy_getinfo(easy_handle, CURLINFO_PRIVATE, &url);

            fprintf(stderr, "result:%d - %s <%s>", msg->data.result, curl_easy_strerror(msg->data.result), url);
            curl_multi_remove_handle(cm, easy_handle);
            curl_easy_cleanup(easy_handle);
        }
        else
        {
            fprintf(stderr, "CURLMsg: %s", msg->msg);
        }

    }

    return 0;
}

技巧

你可能感兴趣的:(libcurl的简单使用)