美文网首页
libcurl的简单使用

libcurl的简单使用

作者: 陈道乐 | 来源:发表于2018-10-31 21:45 被阅读0次

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

    准备

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

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

    步骤

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

    范例

    #include <iostream>
    #include <future>
    #include <string>
    #include <sstream>
    #include <stdexcept>
    #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<std::string>& 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<std::string(std::string)> task(network);
        std::future<std::string> 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 <iostream>
    #include <thread>
    #include <cstring>
    #include <curl/multi.h>
    #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的简单使用

          本文链接:https://www.haomeiwen.com/subject/yspztqtx.html