libcurl 使用方法简介_curlopt_headerfunction-程序员宅基地

技术标签: libcurl  linux  网络编程  

最近项目中需要使用http实现文件的上传功能,使用到了libcurl,转载了一篇博客记录下,便于后续的查看。

 

简介
libcurl是一个跨平台的开源网络协议库,支持http, https, rtsp等多种协议 。libcurl同样支持HTTPS证书授权,HTTP POST, HTTP PUT, FTP 上传, HTTP基本表单上传,代理,cookies,和用户认证。
所以,使用libcurl,可以很简单的完成HTTP的下载工作,为HLS模块的拉流部分提供简单有效的HTTP请求方法。
想要知道更多关于libcurl的介绍,可以到官网 上去了解,在这里不再详述。(官网地址 http://curl.haxx.se/)

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

使用方法
1、easy interface使用方法
easy interface主要方法如下:

1)初始化,这个函数必须是调用的第一个函数,并且它返回一个easy interface
的handle,使用该handle作为easy接口中其他函数的输入。

 CURL *curl_easy_init( );

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

void curl_easy_cleanup(CURL * handle );

3)设置此次传输的一些基本参数,如url地址、http头、cookie信息、发送超时时间等,其中,CURLOPT_URL是必设的选项。
该函数是整个模块的核心,使用该函数,我们可以设置很多相关操作,正是由于该函数的存在,才另libcurl变的简单且具备多种可操作性。
curl_easy_setopt一些经常使用的方式,会在后面补充

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

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

 CURLcode curl_easy_perform(CURL * easy_handle );

5)请求过程中,可以使用下面函数,获取HTTP该次请求的相关信息,包括response
code,下载的URL,下载速度等。该函数对于一次请求不是必须的。

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

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

1、CURLINFO_RESPONSE_CODE 取得response code,要求第 3 个参数是个 long
型的指针。如果TCP就连接不上,值为0。 CURLINFO_EFFECTIVE_URL
取得本最终生效的URL,也即是如果有多次重定向,获取的值为最后一次URL,要求第 3 个参数是个 char 型的指针。
2、CURLINFO_SIZE_DOWNLOAD 获取下载字节数,要求第 3 个参数是个 double
型的指针。注意,这个字节数只能反映最近一次的下载。
3、CURLINFO_SPEED_DOWNLOAD 获取平均下载数据,该选项要求传递一个
double 型参数指针,这个速度不是即时速度,而是下载完成后的速度,单位是 字节/秒
4、CURLINFO_TOTAL_TIME
获取传输总耗时,要求传递一个 double 指针到函数中,这个总的时间里包括了域名解析,以及 TCP 连接过程中所需要的时间。
5、CURLINFO_CONTENT_TYPE 该选项获得 HTTP 中从服务器端收到的头部中的 Content-Type 信息。
6、CURLINFO_CONTENT_LENGTH_DOWNLOAD 获取头部content-length,要求第 3 个参数是个 double
型的指针。如果文件大小无法获取,那么函数返回值为 -1 。

使用上面的几个函数,我们就可以完成一个简单的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);
}


curl_easy_setop简介:
函数原型:

CURLcode curl_easy_setopt(CURL *handle, CURLoption option, parameter);
描述:
这个函数几乎所有的curl 程序都要频繁的使用它,它告诉curl库程序将有如何的行为。 (这个函数有些像ioctl函数)
参数:
1 CURL类型的指针
2 各种CURLoption类型的选项.。这个参数的取值很多,具体的可以查看man手册。
3 parameter 这个参数 既可以是个函数的指针,也可以是某个对象的指针,也可以是个long型的变量。它用什么这取决于第二个参数。

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

CURLOPT_URL 这个选项必须要有,设置请求的URL ,如果URL参数不写上协议头(如 “http://” 或者 "ftp:// 等等),那么函数会自己进行猜解所给的主机上用的是哪一种服务协议。
假如给的这个地址是一个不被支持的协议,那么在其后执行curl_easy_perform() 函数或 curl_multi_perform() 函数时,libcurl将返回错误(CURLE_UNSUPPORTED_PROTOCOL)。 这个选项是唯一一个在 curl_easy_perform()调用之前就一定要设置的选项。
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属性给默认回调函数传递一个已经打开的文件指针,用于将数据输出到文件里。
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;

CURLOPT_HTTPHEADER
libcurl有自己默认的请求头,如果不符合我们的要求,可以使用该选项自定义请求头。可以使用curl_slist_append进行自定义,重设,如果设置请求参数为空,则相当于删除该请求头。
CURLOPT_USERAGENT
该选项要求传递一个以 ‘\0’ 结尾的字符串指针,这个字符串用来在向服务器请求时发送 HTTP 头部中的 User-Agent 信息,有些服务器是需要检测这个信息的,如果没有设置User-Agent,那么服务器拒绝请求。设置后,可以骗过服务器对此的检查。
CURLOPT_VERBOSE
在使用该选项且第 3 个参数为 1 时,curl 库会显示详细的操作信息。这对程序的调试具有极大的帮助。
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指定函数的第一个参数。
CURLOPT_TIMEOUT,CURLOPT_CONNECTIONTIMEOUT
超时相关设置,时间单位为s
1)CURLOPT_TIMEOUT设置整个libcurl传输超时时间。
2)CURLOPT_CONNECTIONTIMEOUT
设置连接等待时间。设置为0,则无限等待。
CURLOPT_FOLLOWLOCATION,CURLOPT_MAXREDIRS
重定向相关设置
1)CURLOPT_FOLLOWLOCATION 设置为非0,响应头信息Location,即curl会自己处理302等重定向
2)CURLOPT_MAXREDIRS指定HTTP重定向的最大次数
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
CURLOPT_POSTFIELDS,CURLOPT_POSTFIELDSIZE
1)CURLOPT_POSTFIELDS 传递一个作为HTTP “POST”操作的所有数据的字符串。
2)CURLOPT_POSTFIELDSIZE 设置POST 字节大小。
CURLOPT_NOBODY
设置该属性即可告诉libcurl我想发起一个HEAD请求 有时候你想查询服务器某种资源的状态,比如某个文件的属性:修改时间,大小等等,但是并不需要具体得到该文件,这时我们仅需要HEAD请求。
CURLOPT_ACCEPT_ENCODING
设置libcurl对特定压缩方式自动解码,支持的方式有: “br, gzip, deflate”. 第3个参数为指定的压缩方式,如果设置为 " ",则表明三种都支持。
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。
CURLOPT_FORBID_REUSE ,CURLOPT_FRESH_CONNEC
如果不使用长连接,需要设置这两个属性
1)CURLOPT_FORBID_REUSE 设置为1,在完成交互以后强迫断开连接,不重用。
2)CURLOPT_FRESH_CONNECT设置为1,强制获取一个新的连接,替代缓存中的连接。
CURLOPT_NOSIGNAL
当多个线程都使用超时处理的时候,同时主线程中有sleep或是wait等操作。如果不设置这个选项,libcurl将会发信号打断这个wait从而可能导致程序crash。 在多线程处理场景下使用超时选项时,会忽略signals对应的处理函数。
CURLOPT_BUFFERSIZE
指定libcurl中接收缓冲区的首选大小(以字节为单位),但是不保证接收数据时每次数据量都能达到这个值。此缓冲区大小默认为CURL_MAX_WRITE_SIZE(16kB)。允许设置的最大缓冲区大小为CURL_MAX_READ_SIZE(512kB)。 允许设置的最小缓冲区大小为1024。
DNS相关选项:

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
CURLOPT_DNS_CACHE_TIMEOUT
设置libcurl DNS缓存超时时间,默认为60秒,即每60秒清一次libcurl自身保存的DNS缓存。
如果设置为0,则不适用DNS缓存,设置为-1,则永远不清缓存。
CURLOPT_DNS_USE_GLOBAL_CACHE
让libcurl使用系统DNS缓存,默认情况下,libcurl使用本身的DNS缓存。
这几个是较常用的选项,当然,libcurl远远不止这几个选项,更详细的使用方法可以参考官网。

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

#include <stdio.h>
#include <string.h>
#include <curl/curl.h>
#include <unistd.h>

//#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不同,它是一种异步,非阻塞的传输方式。

在掌握easy interface的基础上,multi interface的使用也很简单:
1)curl_multi _init初始化一个multi handler对象,
2)初始化多个easy handler对象,使用curl_easy_setopt进行相关设置,
3)调用curl_multi _add_handle把easy handler添加到multi curl对象中
4)添加完毕后执行curl_multi_perform方法进行并发的访问,
5)访问结束后curl_multi_remove_handle移除相关easy curl对象,先用curl_easy_cleanup清除easy handler对象,最后curl_multi_cleanup清除multi handler对象。

multi interface一些函数说明:
1)和easy interface类似,multi 的初始化和清除函数如下:

 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);
当设置好easy模式并准备传输的时候,可以使用curl_multi_add_handle替代curl_easy_perform,这样easy handler则会加入multi栈中。我们能在任何时候增加一个esay handler给multi模式,即使easy已经在执行传输操作了。
也可以在任何时候使用curl_multi_remove_handle将esay handler从multi栈中移出,一旦移出可以再次使用curl_easy_perform来进行传输任务。

3)

CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles);
添加easy handler到multi并不马上开始执行,由curl_multi_perform启动执行。
启动后将执行所有multi stack中的收发事件。如果栈上是空的直接返回。函数参数running_handles会返回当前还未结束的easy handler个数。

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

libcurl中,旧的API使用curl_multi_fdset设置 select或者poll模型触发。
我们看下官网给的example:

#ifdef _WIN32
#define SHORT_SLEEP Sleep(100)
#else
#define SHORT_SLEEP usleep(100000)
#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! */


新的API中,官网更推荐使用curl_multi_wait的方式来实现,实现方便,同时也可以避免select中file descriptors不能大于1024的问题。curl_multi_wait会轮询multi上的所有easy句柄,一直阻塞直到至少有一个被触发或者超时。
如果multi句柄正因为网络延时而挂起,会有一个更短更精确的时间来代替我们自己设置的超时时间timeout_ms。
curl_waitfd参数添加需要监听的socket。
wait返回后,numfds返回被触发的事件数量,若为0表示超时或者没有事件等待。numfds的值包括multi栈上的和extra_fds新加的之和。

看下官网的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 <stdio.h> 
#include <string.h> 
#include <curl/curl.h> 
#include <unistd.h> 
 
//#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<mulit_h_num;i++){ 
        obj[i].fetch_url = strdup( argv[i+1]);//need free 
        char out_filename[1024] ; 
        sprintf(out_filename,"/storage/external_storage/sda4/%s",strrchr( argv[i+1], '/')); 
        printf("----save_file[%d] [%s]\n",i,out_filename); 
        obj[i].save_file = fopen(out_filename,"w"); 
        if(!obj[i].save_file){ 
            printf("ERROR----fail!!!\n"); 
            goto release; 
        } 
         
        obj[i].curl_handle = curl_easy_init(); 
        obj[i].recive_data_fun = recive_data_fun; 
        obj[i].read_head_fun = read_head_fun; 
        if(obj[i].curl_handle){ 
            curl_easy_setopt(obj[i].curl_handle, CURLOPT_NOSIGNAL, 1L);
            curl_easy_setopt(obj[i].curl_handle, CURLOPT_URL, obj[i].fetch_url);//set down load url 
            curl_easy_setopt(obj[i].curl_handle, CURLOPT_WRITEDATA, obj[i].save_file);//set download file 
            curl_easy_setopt(obj[i].curl_handle, CURLOPT_WRITEFUNCTION, obj[i].recive_data_fun);//set call back fun             
#ifdef CURL_DEBUG 
            curl_easy_setopt(obj[i].curl_handle, CURLOPT_VERBOSE, 1); 
#else 
            curl_easy_setopt(obj[i].curl_handle, CURLOPT_HEADERFUNCTION, obj[i].read_head_fun);//set call back fun 
#endif  
            if(multi_handle) curl_multi_add_handle(multi_handle, obj[i].curl_handle);//add task 
        }else{ 
            printf("fetch [%s]----ERROR!!!\n",obj[i].fetch_url ); 
            //goto release; 
        } 
    } 
     
    int still_running,repeats;
    curl_multi_perform(multi_handle, &still_running); 
    do { 
        int numfds = 0; 
        long timeout_ms = CURL_WAIT_TIMEOUT_MSECS; 
        curl_multi_timeout(multi_handle, &timeout_ms);//get timeout ms instead 
        CURLMcode retcode = curl_multi_wait(multi_handle, NULL, 0, timeout_ms, &numfds); 
        if (retcode != CURLM_OK) { 
            printf("ERROR----curl_multi_wait  errorcode[%d]\n",retcode);           
            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 10
           milliseconds. */
        if(!numfds) {
            if(repeats++ > 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<mulit_h_num;i++){ 
        if(obj[i].curl_handle){ 
            curl_multi_remove_handle(multi_handle, obj[i].curl_handle); 
            curl_easy_cleanup(obj[i].curl_handle); 
        } 
        if(obj[i].save_file){ 
            fclose(obj[i].save_file); 
            obj[i].save_file = NULL; 
        } 
        if(obj[i].fetch_url){ 
            free(obj[i].fetch_url); 
            obj[i].fetch_url = NULL; 
        } 
    } 
    if(multi_handle !=NULL){
        curl_multi_cleanup(multi_handle); 
    }
 
    return 0; 
}

 

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/u010977122/article/details/105951583

智能推荐

稀疏编码的数学基础与理论分析-程序员宅基地

文章浏览阅读290次,点赞8次,收藏10次。1.背景介绍稀疏编码是一种用于处理稀疏数据的编码技术,其主要应用于信息传输、存储和处理等领域。稀疏数据是指数据中大部分元素为零或近似于零的数据,例如文本、图像、音频、视频等。稀疏编码的核心思想是将稀疏数据表示为非零元素和它们对应的位置信息,从而减少存储空间和计算复杂度。稀疏编码的研究起源于1990年代,随着大数据时代的到来,稀疏编码技术的应用范围和影响力不断扩大。目前,稀疏编码已经成为计算...

EasyGBS国标流媒体服务器GB28181国标方案安装使用文档-程序员宅基地

文章浏览阅读217次。EasyGBS - GB28181 国标方案安装使用文档下载安装包下载,正式使用需商业授权, 功能一致在线演示在线API架构图EasySIPCMSSIP 中心信令服务, 单节点, 自带一个 Redis Server, 随 EasySIPCMS 自启动, 不需要手动运行EasySIPSMSSIP 流媒体服务, 根..._easygbs-windows-2.6.0-23042316使用文档

【Web】记录巅峰极客2023 BabyURL题目复现——Jackson原生链_原生jackson 反序列化链子-程序员宅基地

文章浏览阅读1.2k次,点赞27次,收藏7次。2023巅峰极客 BabyURL之前AliyunCTF Bypassit I这题考查了这样一条链子:其实就是Jackson的原生反序列化利用今天复现的这题也是大同小异,一起来整一下。_原生jackson 反序列化链子

一文搞懂SpringCloud,详解干货,做好笔记_spring cloud-程序员宅基地

文章浏览阅读734次,点赞9次,收藏7次。微服务架构简单的说就是将单体应用进一步拆分,拆分成更小的服务,每个服务都是一个可以独立运行的项目。这么多小服务,如何管理他们?(服务治理 注册中心[服务注册 发现 剔除])这么多小服务,他们之间如何通讯?这么多小服务,客户端怎么访问他们?(网关)这么多小服务,一旦出现问题了,应该如何自处理?(容错)这么多小服务,一旦出现问题了,应该如何排错?(链路追踪)对于上面的问题,是任何一个微服务设计者都不能绕过去的,因此大部分的微服务产品都针对每一个问题提供了相应的组件来解决它们。_spring cloud

Js实现图片点击切换与轮播-程序员宅基地

文章浏览阅读5.9k次,点赞6次,收藏20次。Js实现图片点击切换与轮播图片点击切换<!DOCTYPE html><html> <head> <meta charset="UTF-8"> <title></title> <script type="text/ja..._点击图片进行轮播图切换

tensorflow-gpu版本安装教程(过程详细)_tensorflow gpu版本安装-程序员宅基地

文章浏览阅读10w+次,点赞245次,收藏1.5k次。在开始安装前,如果你的电脑装过tensorflow,请先把他们卸载干净,包括依赖的包(tensorflow-estimator、tensorboard、tensorflow、keras-applications、keras-preprocessing),不然后续安装了tensorflow-gpu可能会出现找不到cuda的问题。cuda、cudnn。..._tensorflow gpu版本安装

随便推点

物联网时代 权限滥用漏洞的攻击及防御-程序员宅基地

文章浏览阅读243次。0x00 简介权限滥用漏洞一般归类于逻辑问题,是指服务端功能开放过多或权限限制不严格,导致攻击者可以通过直接或间接调用的方式达到攻击效果。随着物联网时代的到来,这种漏洞已经屡见不鲜,各种漏洞组合利用也是千奇百怪、五花八门,这里总结漏洞是为了更好地应对和预防,如有不妥之处还请业内人士多多指教。0x01 背景2014年4月,在比特币飞涨的时代某网站曾经..._使用物联网漏洞的使用者

Visual Odometry and Depth Calculation--Epipolar Geometry--Direct Method--PnP_normalized plane coordinates-程序员宅基地

文章浏览阅读786次。A. Epipolar geometry and triangulationThe epipolar geometry mainly adopts the feature point method, such as SIFT, SURF and ORB, etc. to obtain the feature points corresponding to two frames of images. As shown in Figure 1, let the first image be ​ and th_normalized plane coordinates

开放信息抽取(OIE)系统(三)-- 第二代开放信息抽取系统(人工规则, rule-based, 先抽取关系)_语义角色增强的关系抽取-程序员宅基地

文章浏览阅读708次,点赞2次,收藏3次。开放信息抽取(OIE)系统(三)-- 第二代开放信息抽取系统(人工规则, rule-based, 先关系再实体)一.第二代开放信息抽取系统背景​ 第一代开放信息抽取系统(Open Information Extraction, OIE, learning-based, 自学习, 先抽取实体)通常抽取大量冗余信息,为了消除这些冗余信息,诞生了第二代开放信息抽取系统。二.第二代开放信息抽取系统历史第二代开放信息抽取系统着眼于解决第一代系统的三大问题: 大量非信息性提取(即省略关键信息的提取)、_语义角色增强的关系抽取

10个顶尖响应式HTML5网页_html欢迎页面-程序员宅基地

文章浏览阅读1.1w次,点赞6次,收藏51次。快速完成网页设计,10个顶尖响应式HTML5网页模板助你一臂之力为了寻找一个优质的网页模板,网页设计师和开发者往往可能会花上大半天的时间。不过幸运的是,现在的网页设计师和开发人员已经开始共享HTML5,Bootstrap和CSS3中的免费网页模板资源。鉴于网站模板的灵活性和强大的功能,现在广大设计师和开发者对html5网站的实际需求日益增长。为了造福大众,Mockplus的小伙伴整理了2018年最..._html欢迎页面

计算机二级 考试科目,2018全国计算机等级考试调整,一、二级都增加了考试科目...-程序员宅基地

文章浏览阅读282次。原标题:2018全国计算机等级考试调整,一、二级都增加了考试科目全国计算机等级考试将于9月15-17日举行。在备考的最后冲刺阶段,小编为大家整理了今年新公布的全国计算机等级考试调整方案,希望对备考的小伙伴有所帮助,快随小编往下看吧!从2018年3月开始,全国计算机等级考试实施2018版考试大纲,并按新体系开考各个考试级别。具体调整内容如下:一、考试级别及科目1.一级新增“网络安全素质教育”科目(代..._计算机二级增报科目什么意思

conan简单使用_apt install conan-程序员宅基地

文章浏览阅读240次。conan简单使用。_apt install conan