libcurl 使用方法简介

libcurl 使用方法简介libcurl 使用方法简介背景简介使用方法 1 easyinterfac 使用方法 2 multiinterfa 使用方法背景最近想做一个简单的 HLS 拉流程序 HTTP 的下载部分觉得采用 libcurl 来进行比较合适及方便 所以先介绍 libcurl 的基本用法 然后使用 libcurl 完成一个简单的下载小程序 简介 libcurl 是一个跨平台的开源网络协议库 支持 http https rt

背景

最近想做一个简单的HLS拉流程序,HTTP的下载部分觉得采用libcurl来进行比较合适及方便,所以先介绍libcurl的基本用法,然后使用libcurl完成一个简单的下载小程序。

简介

libcurl主要提供了两种发送HTTP请求的方式,分别是easy interface方式和multi interface方式,前者是采用阻塞的方式发送单条请求,后者采用组合的方式可以一次性发送多条请求数据,且多个下载请求是异步进行的。

使用方法

1、easy interface使用方法

easy interface主要方法如下:

 CURL *curl_easy_init( ); 

2)当操作完成时,此调用必须有相应的调用curl_easy_cleanup() 来释放handle。

void curl_easy_cleanup(CURL * handle ); 
 CURLcode curl_easy_setopt(CURL *handle, CURLoption option, parametr); 

4)在上述准备工作已经完成后,可以调用curl_easy_perform函数,则会开始HTTP的请求工作。该接口是一个阻塞的接口。

 CURLcode curl_easy_perform(CURL * easy_handle ); 
 CURLcode curl_easy_getinfo(CURL *curl, CURLINFO info, ... ); 

第 2 个参数有众多选项,每个选项都有其相应的含义,第3个参数根据参数2,会有不同类型。常用的info如下:

使用上面的几个函数,我们就可以完成一个简单的HTTP下载程序:

CURL *curl = curl_easy_init(); if(curl) { CURLcode res; curl_easy_setopt(curl, CURLOPT_URL, "http://example.com"); res = curl_easy_perform(curl); curl_easy_cleanup(curl); } 
CURLcode curl_easy_setopt(CURL *handle, CURLoption option, parameter); 

下面介绍几个常用的参数及使用方法:

  1. CURLOPT_URL 这个选项必须要有,设置请求的URL ,如果URL参数不写上协议头(如 “http://” 或者 “ftp:// 等等),那么函数会自己进行猜解所给的主机上用的是哪一种服务协议。
    假如给的这个地址是一个不被支持的协议,那么在其后执行curl_easy_perform() 函数或 curl_multi_perform() 函数时,libcurl将返回错误(CURLE_UNSUPPORTED_PROTOCOL)。 这个选项是唯一一个在 curl_easy_perform()调用之前就一定要设置的选项。

  1. CURLOPT_WRITEFUNCTION,CURLOPT_WRITEDATA
    1)CURLOPT_WRITEFUNCTION选项用于设置接收数据回调函数,回调函数原型为: size_t function(void *ptr, size_t size, size_t nmemb, void *stream); 函数将在libcurl接收到数据后被调用,因此函数多做数据保存的功能,如处理下载文件。
    2) CURLOPT_WRITEDATA选项用于指定CURLOPT_WRITEFUNCTION函数中的stream指针的来源。
    3)如果没有通过CURLOPT_WRITEFUNCTION属性给easy handle设置回调函数,libcurl会提供一个默认的回调函数,它只是简单的将接收到的数据打印到标准输出。也可以通过CURLOPT_WRITEDATA属性给默认回调函数传递一个已经打开的文件指针,用于将数据输出到文件里。






  1. CURLOPT_HEADERFUNCTION,CURLOPT_HEADERDATA
    1)CURLOPT_HEADERFUNCTION设置接收到http头的回调函数,原型为: size_t function(void *ptr,size_t size,size_t nmemb, void *stream); libcurl一旦接收到http 头部数据后将调用该函数。
    2)CURLOPT_HEADERDATA传递指针给libcurl,该指针表明CURLOPT_HEADERFUNCTION函数的stream指针的来源。




和上面两组类似的,这样对应的回调选项还有很多,使用方法也类似,如: CURLOPT_READFUNCTION/ CURLOPT_READDATA;

  1. CURLOPT_HTTPHEADER
    libcurl有自己默认的请求头,如果不符合我们的要求,可以使用该选项自定义请求头。可以使用curl_slist_append进行自定义,重设,如果设置请求参数为空,则相当于删除该请求头。

  1. CURLOPT_USERAGENT
    该选项要求传递一个以 ‘\0’ 结尾的字符串指针,这个字符串用来在向服务器请求时发送 HTTP 头部中的 User-Agent 信息,有些服务器是需要检测这个信息的,如果没有设置User-Agent,那么服务器拒绝请求。设置后,可以骗过服务器对此的检查。

  1. CURLOPT_VERBOSE
    在使用该选项且第 3 个参数为 1 时,curl 库会显示详细的操作信息。这对程序的调试具有极大的帮助。

  1. CURLOPT_NOPROGRESS,CURLOPT_PROGRESSFUNCTION,CURLOPT_PROGRESSDATA
    这三个选项和跟数据传输进度相关。
    1)CURLOPT_PROGRESSFUNCTION设置回调函数,函数原型: int progress_callback(void *clientp, double dltotal, double dlnow, double ultotal, double ulnow); progress_callback正常情况下每秒被libcurl调用一次。
    2)CURLOPT_NOPROGRESS必须被设置为false才会启用该功能,
    3)CURLOPT_PROGRESSDATA指定的参数将作为CURLOPT_PROGRESSFUNCTION指定函数的第一个参数。








  1. CURLOPT_TIMEOUT,CURLOPT_CONNECTIONTIMEOUT
    超时相关设置,时间单位为s
    1)CURLOPT_TIMEOUT设置整个libcurl传输超时时间。
    2)CURLOPT_CONNECTIONTIMEOUT
    设置连接等待时间。设置为0,则无限等待。








  1. CURLOPT_FOLLOWLOCATION,CURLOPT_MAXREDIRS
    重定向相关设置
    1)CURLOPT_FOLLOWLOCATION 设置为非0,响应头信息Location,即curl会自己处理302等重定向
    2)CURLOPT_MAXREDIRS指定HTTP重定向的最大次数






  1. CURLOPT_RANGE ,CURLOPT_RESUME_FROM/ CURLOPT_RESUME_FROM_LARGE 断点续传相关设置。
    1)CURLOPT_RANGE 指定char *参数传递给libcurl,用于指明http请求的range
    2)CURLOPT_RESUME_FROM传递一个long参数作为偏移量给libcurl,指定开始进行传输的位置。CURLOPT_RESUME_FROM大小限制为2G,超过可以使用CURLOPT_RESUME_FROM_LARGE




  1. CURLOPT_POSTFIELDS,CURLOPT_POSTFIELDSIZE
    1)CURLOPT_POSTFIELDS 传递一个作为HTTP “POST”操作的所有数据的字符串。
    2)CURLOPT_POSTFIELDSIZE 设置POST 字节大小。




  1. CURLOPT_NOBODY
    设置该属性即可告诉libcurl我想发起一个HEAD请求 有时候你想查询服务器某种资源的状态,比如某个文件的属性:修改时间,大小等等,但是并不需要具体得到该文件,这时我们仅需要HEAD请求。

  1. CURLOPT_ACCEPT_ENCODING
    设置libcurl对特定压缩方式自动解码,支持的方式有: “br, gzip, deflate”. 第3个参数为指定的压缩方式,如果设置为 ” “,则表明三种都支持。

  1. CURLOPT_MAX_RECV_SPEED_LARGE,CURLOPT_MAX_SEND_SPEED_LARGE
    限速相关设置
    1)CURLOPT_MAX_RECV_SPEED_LARGE,指定下载过程中最大速度,单位bytes/s。
    2)CURLOPT_MAX_SEND_SPEED_LARG,指定上传过程中最大速度,单位bytes/s。






  1. CURLOPT_FORBID_REUSE ,CURLOPT_FRESH_CONNEC
    如果不使用长连接,需要设置这两个属性
    1)CURLOPT_FORBID_REUSE 设置为1,在完成交互以后强迫断开连接,不重用。
    2)CURLOPT_FRESH_CONNECT设置为1,强制获取一个新的连接,替代缓存中的连接。






  1. CURLOPT_NOSIGNAL
    当多个线程都使用超时处理的时候,同时主线程中有sleep或是wait等操作。如果不设置这个选项,libcurl将会发信号打断这个wait从而可能导致程序crash。 在多线程处理场景下使用超时选项时,会忽略signals对应的处理函数。

  1. CURLOPT_BUFFERSIZE
    指定libcurl中接收缓冲区的首选大小(以字节为单位),但是不保证接收数据时每次数据量都能达到这个值。此缓冲区大小默认为CURL_MAX_WRITE_SIZE(16kB)。允许设置的最大缓冲区大小为CURL_MAX_READ_SIZE(512kB)。 允许设置的最小缓冲区大小为1024。

DNS相关选项:

  1. CURLOPT_IPRESOLVE
    指定libcurl 域名解析模式。支持的选项有:
    1)CURL_IPRESOLVE_WHATEVER:默认值,相当于PF_UNSPEC,支持IPv4/v6,具体以哪个优先需要看libc底层实现,Android中默认以IPv6优先,当IPv6栈无法使用时,libcurl会用IPv4。
    2)CURL_IPRESOLVE_V4:.仅请求A记录,即只解析为IPv4地址。
    3)CURL_IPRESOLVE_V6:.仅请求AAAA记录,即只解析为IPv6地址。
    注意:该功能生效的前提是libcurl支持IPv6,需要在curl/lib/curl_config.h配置#define ENABLE_IPV6 1










  1. CURLOPT_DNS_CACHE_TIMEOUT
    设置libcurl DNS缓存超时时间,默认为60秒,即每60秒清一次libcurl自身保存的DNS缓存。
    如果设置为0,则不适用DNS缓存,设置为-1,则永远不清缓存。




  1. CURLOPT_DNS_USE_GLOBAL_CACHE
    让libcurl使用系统DNS缓存,默认情况下,libcurl使用本身的DNS缓存。

这几个是较常用的选项,当然,libcurl远远不止这几个选项,更详细的使用方法可以参考官网。

最后使用easy interface完成下载的简单代码:

#include 
  
    #include 
   
     #include 
    
      #include 
     
       //#define CURL_DEBUG 1 #define CURL_WAIT_TIMEOUT_MSECS 60000 //60s #define CURL_MULIT_MAX_NUM 5 static size_t recive_data_fun( void *ptr, size_t size, size_t nmemb, void *stream){ return fwrite(ptr,size,nmemb,(FILE*)stream); } static size_t read_head_fun( void *ptr, size_t size, size_t nmemb, void *stream){ char head[2048] = {0}; memcpy(head,ptr,size*nmemb+1); printf(" %s \n",head); return size*nmemb; } int main(int argc, char argv) { if(argc < 3){ printf("arg1 is url; arg2 is out file\n"); return -1; } char* url = strdup( argv[1]); char* filename= strdup(argv[2]); CURL* curl_handle; CURLcode res; //int FILE* save_file = fopen(filename,"w"); if(save_file == NULL){ printf("open save file fail!\n"); return -1; } curl_handle = curl_easy_init(); if(curl_handle){ curl_easy_setopt(curl_handle, CURLOPT_URL, url);//set down load url curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, save_file);//set download file curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, recive_data_fun);//set call back fun curl_easy_setopt(curl_handle, CURLOPT_HEADERFUNCTION, read_head_fun);//set call back fun #ifdef CURL_DEBUG curl_easy_setopt(curl_handle, CURLOPT_VERBOSE, 1); #endif //start down load res = curl_easy_perform(curl_handle); printf("curl fetch code %d\n",res); } //release if(save_file){ fclose(save_file); save_file = NULL; } if(curl_handle){ curl_easy_cleanup(curl_handle); } if(url){ free(url); } return 0; } 
      
     
    
  

2、multi interface使用方法

multi interface的使用是在easy interface的基础之上,将多个easy handler加入到一个stack中,同时发送请求。与easy interface不同,它是一种异步,非阻塞的传输方式。

 CURLM *curl_multi_init( ); CURLMcode curl_multi_cleanup( CURLM*multi_handle ); 

2)

CURLMcode curl_multi_add_handle(CURLM *multi_handle, CURL *easy_handle); CURLMcode curl_multi_remove_handle(CURLM *multi_handle, CURL *easy_handle); 

3)

CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles); 

4)等待及超时

CURLMcode curl_multi_fdset(CURLM *multi_handle, fd_set *read_fd_set, fd_set *write_fd_set, fd_set *exc_fd_set, int *max_fd); CURLMcode curl_multi_wait(CURLM *multi_handle, struct curl_waitfd extra_fds[], unsigned int extra_nfds, int timeout_ms, int *numfds); 
#ifdef _WIN32 #define SHORT_SLEEP Sleep(100) #else #define SHORT_SLEEP usleep() #endif fd_set fdread; fd_set fdwrite; fd_set fdexcep; int maxfd = -1; long curl_timeo; curl_multi_timeout(multi_handle, &curl_timeo); if(curl_timeo < 0) curl_timeo = 1000; timeout.tv_sec = curl_timeo / 1000; timeout.tv_usec = (curl_timeo % 1000) * 1000; FD_ZERO(&fdread); FD_ZERO(&fdwrite); FD_ZERO(&fdexcep); /* get file descriptors from the transfers */ mc = curl_multi_fdset(multi_handle, &fdread, &fdwrite, &fdexcep, &maxfd); if(maxfd == -1) { SHORT_SLEEP; rc = 0; } else rc = select(maxfd+1, &fdread, &fdwrite, &fdexcep, &timeout); switch(rc) { case -1: /* select error */ break; case 0: default: /* timeout or readable/writable sockets */ curl_multi_perform(multi_handle, &still_running); break; } /* if there are still transfers, loop! */ 

看下官网的example:

CURL *easy_handle; CURLM *multi_handle; /* add the individual easy handle */ curl_multi_add_handle(multi_handle, easy_handle); do { CURLMcode mc; int numfds; mc = curl_multi_perform(multi_handle, &still_running); if(mc == CURLM_OK ) { /* wait for activity, timeout or "nothing" */ mc = curl_multi_wait(multi_handle, NULL, 0, 1000, &numfds); } if(mc != CURLM_OK) { fprintf(stderr, "curl_multi failed, code %d.n", mc); break; } /* 'numfds' being zero means either a timeout or no file descriptors to wait for. Try timeout on first occurrence, then assume no file descriptors and no file descriptors to wait for means wait for 100 milliseconds. */ if(!numfds) { repeats++; /* count number of repeated zero numfds */ if(repeats > 1) { WAITMS(100); /* sleep 100 milliseconds */ } } else repeats = 0; } while(still_running); curl_multi_remove_handle(multi_handle, easy_handle); 

对于select和curl_multi_wait两种方式,都可以使用curl_multi_timeout获取一个合适的超时时间,当然,超时时间也可以我们自己设置。

5)

CURLMsg *curl_multi_info_read( CURLM *multi_handle, int *msgs_in_queue); 

我们可以调用curl_multi_info_read获取每一个执行完成的操作信息。在完成后即将其移除multi stack。

/* call curl_multi_perform or curl_multi_socket_action first, then loop through and check if there are any transfers that have completed */ struct CURLMsg *m; do { int msgq = 0; m = curl_multi_info_read(multi_handle, &msgq); if(m && (m->msg == CURLMSG_DONE)) { CURL *e = m->easy_handle; transfers--; curl_multi_remove_handle(multi_handle, e); curl_easy_cleanup(e); } } while(m); 

最后使用multi interface完成并发下载的简单代码:

#include 
  
    #include 
   
     #include 
    
      #include 
     
       //#define CURL_DEBUG 1 #define CURL_WAIT_TIMEOUT_MSECS 1000 //1s #define CURL_MULIT_MAX_NUM 5 typedef struct curl_obj{ CURL* curl_handle; FILE* save_file; char* fetch_url; size_t (*recive_data_fun)( void *ptr, size_t size, size_t nmemb, void *stream); size_t (*read_head_fun)( void *ptr, size_t size, size_t nmemb, void *stream); }curl_obj; static size_t recive_data_fun( void *ptr, size_t size, size_t nmemb, void *stream){ return fwrite(ptr,size,nmemb,(FILE*)stream); } static size_t read_head_fun( void *ptr, size_t size, size_t nmemb, void *stream){ char head[2048] = {0}; memcpy(head,ptr,size*nmemb+1); printf(" %s \n",head); return size*nmemb; } int main(int argc, char argv) { if(argc < 3){ printf("ERROR----arg1 is url; arg2 is out file\n"); return -1; } char* outfile_name[CURL_MULIT_MAX_NUM] = {0}; curl_obj obj[CURL_MULIT_MAX_NUM]; int mulit_h_num = ((argc -1) < CURL_MULIT_MAX_NUM)? (argc -1):CURL_MULIT_MAX_NUM; CURLM *multi_handle = curl_multi_init(); for(int i=0;i 
      
        60){ printf("ERROR----timeout break!!! \n"); break; }else{ usleep(10*1000); /* sleep 10 milliseconds */ continue; } } else{ repeats = 0; } retcode = curl_multi_perform(multi_handle, &still_running); if (retcode != CURLM_OK) { printf("ERROR----curl_multi_perform errorcode[%d]\n",retcode); break; } //printf("still_running[%d]\tnumfds[%d]\n",still_running,numfds ); int msgs_left = 0; CURLMsg *msg = NULL; while ((msg = curl_multi_info_read(multi_handle, &msgs_left)) != NULL){ if (msg->msg == CURLMSG_DONE) { long http_response_code = -1; char* http_url = NULL; curl_easy_getinfo(msg->easy_handle, CURLINFO_RESPONSE_CODE, &http_response_code); curl_easy_getinfo(msg->easy_handle, CURLINFO_EFFECTIVE_URL, &http_url); printf("[%s]fetch done, response[%d]\n",http_url,http_response_code ); } } } while (still_running); release: printf("release\n"); for(int i=0;i 
        
       
      
     
    
  
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/221970.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月17日 下午4:52
下一篇 2026年3月17日 下午4:52


相关推荐

  • python conda 命令

    python conda 命令1 首先在所在系统中安装 Anaconda 可以打开命令行输入 conda V 检验是否安装以及当前 conda 的版本 2 conda 常用的命令 1 condalist 查看安装了哪些包 2 condaenvlist 或 condainfo e 查看当前存在哪些虚拟环境 3 condaupdatec 检查更新当前 conda3 创建 pytho

    2026年3月16日
    2
  • Flume 入门–几种不同的Sinks

    Flume 入门–几种不同的Sinks主要介绍几种常见 Flume 的 Sink 汇聚点 1 LoggerSink nbsp 记录 INFO 级别的日志 一般用于调试 前面介绍 Source 时候用到的 Sink 都是这个类型的 Sink 必须配置的属性 属性说明 nbsp nbsp nbsp nbsp nbsp nbsp nbsp nbsp nbsp channel nbsp nbsp nbsp nbsp nbsp nbsp nbsp nbsp nbsp nbsp nbsp nbsp nbsp nbsp nbsp nbsp type nbsp nbsp nbsp nbsp nbsp nbsp Thecomponent needstobelog nbsp nbsp

    2026年3月18日
    4
  • JavaScript零基础入门 4:javascript对象有哪些

    JavaScript零基础入门 4:javascript对象有哪些Java 学习路线配套文章 Java 学习路线总结 搬砖工逆袭 Java 架构师 全网最强 基础推荐 Java 基础教程系列 实战推荐 SpringBoot 基础教程 简介 Java 领域优质创作者 CSDN 哪吒公众号作者 Java 架构师奋斗者 扫描主页左侧二维码 加入群聊 一起学习 一起进步 欢迎点赞 收藏 留言 目录一 对象 1 对象的分类 2 创建对象 3 属性 4 读取对象中的属性 5 修改对象的属性值 6 删除对象的

    2026年3月19日
    1
  • linux vi 替换命令_vi替换命令转移

    linux vi 替换命令_vi替换命令转移原文地址:http://www.cnblogs.com/afant/archive/2009/03/11/1408745.html:s/^.*$/\L&/100#将100行内的小写转换成大写vi/vim中可以使用:s命令来替换字符串。:s/vivian/sky/替换当前行第一个vivian为sky:s/vivian/sky/g替换当前行所有vi

    2026年2月15日
    10
  • 美工失业预警?即梦AI 3.0海报、封面、包装全搞定!

    美工失业预警?即梦AI 3.0海报、封面、包装全搞定!

    2026年3月12日
    3
  • 组合数公式(代码实现)[通俗易懂]

    组合数公式(代码实现)[通俗易懂]比如求CnmC_{n}^{m}Cnm​,代码如下:llcombi(intn,intm){//求组合数的函数llcnt=1;for(inti=0;i<m;i++){cnt*=n-i;cnt/=i+1;}returncnt;}…

    2022年7月25日
    22

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号