HTTP協議 學習:2-基於libcurl的開發


HTTP協議 學習:2-基於libcurl的開發

背景

上一講我們介紹了HTTP報文的一些內容,這一講我們基於http有關的開源庫,進行HTTP通信。最后再完成一個簡單的下載小程序。

ref : https://blog.csdn.net/myvest/article/details/82899788

curl 簡介

curl是一個跨平台的開源網絡協議庫,支持http, https, rtsp等多種協議 。libcurl同樣支持HTTPS證書授權,HTTP POST, HTTP PUT, FTP 上傳, HTTP基本表單上傳,代理,cookies和用戶認證。

libcurl主要提供了兩種發送HTTP請求的方式,分別是easy interface方式和multi interface方式。更多內容可以參考:《The libcurl API》

  • easy interface:采用阻塞的方式發送單條請求
  • multi interface:采用組合的方式可以一次性發送多條請求數據,支持多個下載請求是異步進行的。

基於easy interface 的 使用

初始化與釋放有關的句柄(handle)

 CURL *curl_easy_init( );

描述:初始化,並且它返回一個easy interface 的 handle, 使用該handle作為easy接口中其他函數的輸入。

void curl_easy_cleanup(CURL * handle );

描述:當操作完成時,釋放handle。

設置HTTP傳輸參數

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

描述:設置此次傳輸的一些基本參數,如url地址、http頭、cookie信息、發送超時時間等,其中,CURLOPT_URL是必設的選項。
該函數是整個模塊的核心,使用該函數,我們可以設置很多相關操作,正是由於該函數的存在,才使得libcurl變的簡單且具備多種可操作性。curl_easy_setopt一些經常使用的方式,會在后面補充

開始HTTP請求

CURLcode curl_easy_perform(CURL * easy_handle);

描述:在上述准備工作(curl_easy_setopt)已經完成后,可以調用curl_easy_perform函數,則會開始HTTP的請求工作。該接口是一個阻塞的接口。

(可選)獲取相關信息

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

描述:請求過程中,可以使用下面函數,獲取HTTP該次請求的相關信息,包括response
code,下載的URL,下載速度等。該函數對於一次請求不是必須的。

第 2 個參數有眾多選項,每個選項都有其相應的含義,第3個參數根據參數2,會有不同類型。常用的info如下:

  • CURLINFO_RESPONSE_CODE: 取得response code,要求第 3 個參數是個 long
    型的指針。如果TCP就連接不上,值為0。 CURLINFO_EFFECTIVE_URL
    取得本最終生效的URL,也即是如果有多次重定向,獲取的值為最后一次URL,要求第 3 個參數是個 char 型的指針。
  • CURLINFO_SIZE_DOWNLOAD :獲取下載字節數,要求第 3 個參數是個 double
    型的指針。注意,這個字節數只能反映最近一次的下載。
  • CURLINFO_SPEED_DOWNLOAD :獲取平均下載數據,該選項要求傳遞一個
    double 型參數指針,這個速度不是即時速度,而是下載完成后的速度,單位是 字節/秒
  • CURLINFO_TOTAL_TIME :獲取傳輸總耗時,要求傳遞一個 double 指針到函數中,這個總的時間里包括了域名解析,以及 TCP 連接過程中所需要的時間。
  • CURLINFO_CONTENT_TYPE :獲得 HTTP 中從服務器端收到的頭部中的 Content-Type 信息。
  • 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庫程序將有如何的行為。 (這個函數有些像ioctl函數)
參數解析:
handle: CURL類型的指針
option:各種CURLoption類型的選項.。
parameter: 參數,根據option的不同可以取不同的值。

下面介紹幾個常用的參數及使用方法:

libcurl遠遠不止這幾個選項,更詳細的使用方法可以參考官方文檔

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緩存。

例程:使用easy interface完成下載

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <curl/curl.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;
}

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對象。

初始化 與 釋放

和easy interface類似,multi 的初始化和清除函數如下:

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

添加 與 移除

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來進行傳輸任務。

開始HTTP並發請求

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

添加easy handler到multi並不馬上開始執行,由curl_multi_perform啟動執行。
啟動后將執行所有multi stack中的收發事件。如果棧上是空的直接返回。函數參數running_handles會返回當前還未結束的easy handler個數。

等待及超時

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獲取一個合適的超時時間,當然,超時時間也可以我們自己設置。

(可選)獲取操作信息

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM