libcurl的简单使用

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;
}

技巧

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 176,506评论 25 709
  • 用两张图告诉你,为什么你的 App 会卡顿? - Android - 掘金 Cover 有什么料? 从这篇文章中你...
    hw1212阅读 14,519评论 2 59
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 136,174评论 19 139
  • 20180301 1.阅读摘录 我的目标是在一个极度透明和极度开放的环境当中,形成一个有意义的关系,共同来做有意义...
    践行哲阅读 1,261评论 3 2
  • 这一章的阅读加速器是:空白卡片法。 1、 首先选择一个章节或时间范围; 2、 其次阅读时卡片遮住已阅读部分; 3、...
    丹阔阅读 1,427评论 0 0

友情链接更多精彩内容