(轉) c/c++調用libcurl庫發送http請求的兩種基本用法

libcurl主要提供了兩種發送http請求的方式,分別是Easy interface方式和multi interface方式,前者是採用阻塞的方式發送單條數據,後者採用組合的方式能夠一次性發送多條數據mysql

1、Easy interface
sql

libcurl的easy interface是最基本的用法,簡要流程爲:
一、在主線程中調用curl_global_init(CURL_GLOBAL_ALL)初始化
二、調用curl_easy_init獲取一個句柄;
三、調用curl_easy_setopt函數設置這次傳輸的一些基本參數,如url地址、http頭、cookie信息、發送超時時間等,其中,CURLOPT_URL是必設的選項;
四、設置完成後,調用curl_easy_perform函數發送數據;
五、數據發送完畢後,調用curl_easy_cleanup清空句柄;
六、調用curl_global_cleanup()作清理工做。
cookie

實現代碼:多線程

  1. bool send_easy_hanler(char* post_url, req_t* req)  
  2. {  
  3.   //easy handler的句柄  
  4.   CURL* curl = NULL;  
  5.   CURLcode res = CURLE_OK;  
  6.   //HTTP報文頭  
  7.   struct curl_slist* headers = NULL;  
  8.   char tmp_str[256] = { 0 };  
  9.   
  10.   //構建HTTP報文頭  
  11.   snprintf(tmp_str, sizeof(tmp_str), "User-Agent: %s", req->user_agent_);  
  12.   headers = curl_slist_append(headers, tmp_str);  
  13.   snprintf(tmp_str, sizeof(tmp_str), "Accept-Language: %s", req->language_);  
  14.   headers = curl_slist_append(headers, tmp_str);  
  15.   snprintf(tmp_str, sizeof(tmp_str), "X-FORWORDED-FOR: %s", req->ip_.c_str());  
  16.   headers = curl_slist_append(headers, tmp_str);  
  17.   
  18.   /*這個函數只能用一次,若是這個函數在curl_easy_init函數調用時還沒調用, 
  19.   它講由libcurl庫自動調用,因此多線程下最好在主線程中調用一次該函數以防止在線程 
  20.   中curl_easy_init時屢次調用*/  
  21.   curl_global_init(CURL_GLOBAL_ALL);  
  22.     
  23.   //初始化easy handler句柄  
  24.   curl = curl_easy_init();  
  25.   if (curl) {  
  26.     //設置post請求的url地址  
  27.     curl_easy_setopt(curl, CURLOPT_URL, post_url);  
  28.     //設置HTTP頭  
  29.     curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);  
  30.     //設置發送超時時間  
  31.     curl_easy_setopt(curl, CURLOPT_TIMEOUT, 1);  
  32.   
  33.     //執行單條請求  
  34.     res = curl_easy_perform(curl);  
  35.     if (res != CURLE_OK) {  
  36.       //curl_easy_strerror進行出錯打印  
  37.       LOG(WARNING) << "curl_easy_perform() failed:" << curl_easy_strerror(res);  
  38.     }  
  39.       
  40.     curl_slist_free_all(headers);  
  41.       
  42.     //這個調用用來結束一個會話.與curl_easy_init配合着用  
  43.     curl_easy_cleanup(curl);  
  44.       
  45.     //在結束libcurl使用的時候,用來對curl_global_init作的工做清理。相似於close的函數  
  46.     curl_global_cleanup();  
  47. }  


2、multi interface
multi interface提供了多種easy interface沒有的特性,主要是
一、提供了pull接口,使用libcurl的程序可以決定什麼時候何處調用libcurl來get/send數據
二、在同一線程中實現多條數據同時發送,且並無使得程序更加複雜
三、程序能夠在本身的文件描述符和curl的文件描述符中同時等待執行
四、提供基於事件的處理、擴大傳輸規模到數千個併發鏈接

multi接口的使用會比easy 接口稍微複雜點,畢竟multi接口是依賴easy接口的,簡要流程爲:
一、使用curl_multi_init建立一個multi handle,這個handler會在後續的curl_multi_*函數中使用multi handler能夠同時併發傳輸多條數據,每一條單獨的數據是由一個easy handler建立;
二、須要事先將須要傳輸的全部easyhandler建立好,並使用curl_easy_setopt設置各自屬性,接着調用curl_multi_add_handle函數逐個添加到multi handle中;
三、調用curl_multi_perform進程數據傳輸,傳輸過程當中將會調用每個easy handler設置的回調函數或者配置內容,程序經過函數curl_multi_fdset、select()提取信息來判斷什麼時候進行數據傳輸等操做,函數curl_multi_perform的一個輸入參數儲存仍在進行傳輸的數據量,經過讀取該變量,能夠判斷multi handles是否運行完畢,傳輸完畢不表明傳輸成功,可能有一個或多個傳輸失敗;
四、調用函數curl_multi_info_read能夠獲取當前或以前傳輸的信息,重複調用該函數直到該消息隊列爲空,每一條返回信息都包含對應的easl handler的傳輸狀況;
五、當一個easy handler傳輸完成,此easy handler仍然仍然停留在multi stack中,須要調用curl_multi_remove_handle將其從multi stack中移除,而後調用curl_easy_cleanup將其關閉;
六、當multi stack中的全部傳輸都完成時,調用 curl_multi_cleanup關閉multi handler,須要注意的是事先要調用curl_easy_cleanup逐個清空全部easy handler。
併發

源碼:oracle

 

    1. #include <errno.h>  
    2. #include <stdlib.h>  
    3. #include <string.h>  
    4. #include <curl/multi.h>  
    5.   
    6. static const char *urls[] = {  
    7.   "http://www.microsoft.com",  
    8.   "http://www.opensource.org",  
    9.   "http://www.google.com",  
    10.   "http://www.yahoo.com",  
    11.   "http://www.ibm.com",  
    12.   "http://www.mysql.com",  
    13.   "http://www.oracle.com",  
    14.   "http://www.ripe.net",  
    15. };  
    16.   
    17. #define MAX 8 /* number of simultaneous transfers */  
    18. #define CNT sizeof(urls)/sizeof(char*) /* total number of transfers to do */  
    19.   
    20. /*此函數讀取libcurl發送數據後的返回信息,若是不設置此函數, 
    21. 那麼返回值將會輸出到控制檯,影響程序性能*/  
    22. static size_t cb(char *d, size_t n, size_t l, void *p)  
    23. {  
    24.   /* take care of the data here, ignored in this example */  
    25.   (void)d;  
    26.   (void)p;  
    27.   return n*l;  
    28. }  
    29.   
    30. //設置單個easy handler的屬性添加單個easy handler到multi handler中,  
    31. static void init(CURLM *cm, int i)  
    32. {  
    33.   CURL *eh = curl_easy_init();  
    34.   
    35.   curl_easy_setopt(eh, CURLOPT_WRITEFUNCTION, cb);  
    36.   curl_easy_setopt(eh, CURLOPT_HEADER, 0L);  
    37.   curl_easy_setopt(eh, CURLOPT_URL, urls[i]);  
    38.   curl_easy_setopt(eh, CURLOPT_PRIVATE, urls[i]);  
    39.   curl_easy_setopt(eh, CURLOPT_VERBOSE, 0L);  
    40.   
    41.   //添加easy handler 到multi handler中  
    42.   curl_multi_add_handle(cm, eh);  
    43. }  
    44.   
    45. int main(void)  
    46. {  
    47.   CURLM *cm;  
    48.   CURLMsg *msg;  
    49.   long curl_timeo;  
    50.   unsigned int C=0;  
    51.   int max_fd, msgs_left, still_running = -1;//still_running判斷multi handler是否傳輸完畢  
    52.   fd_set fd_read, fd_write, fd_except;  
    53.   struct timeval T;  
    54.   
    55.   curl_global_init(CURL_GLOBAL_ALL);  
    56.   
    57.   cm = curl_multi_init();  
    58.   
    59.   //如今multi handler的最大鏈接數  
    60.   curl_multi_setopt(cm, CURLMOPT_MAXCONNECTS, (long)MAX);  
    61.   
    62.   for(C = 0; C < MAX; ++C) {  
    63.     init(cm, C);  
    64.   }  
    65.   
    66.     
    67.   do{  
    68.     curl_multi_perform(cm, &still_running);  
    69.   
    70.     if(still_running) {  
    71.       FD_ZERO(&fd_read);  
    72.       FD_ZERO(&fd_write);  
    73.       FD_ZERO(&fd_except);  
    74.   
    75.       //獲取multi curl須要監聽的文件描述符集合 fd_set  
    76.       if(!curl_multi_fdset(cm, &fd_read, &fd_write, &fd_except, &max_fd)) {  
    77.         fprintf(stderr, "E: curl_multi_fdset\n");  
    78.         return EXIT_FAILURE;  
    79.       }  
    80.   
    81.       if(!curl_multi_timeout(cm, &curl_timeo)) {  
    82.         fprintf(stderr, "E: curl_multi_timeout\n");  
    83.         return EXIT_FAILURE;  
    84.       }  
    85.       if(curl_timeo == -1)  
    86.         curl_timeo = 100;  
    87.   
    88.       //若是max_fd返回-1,休眠一段時間後繼續執行curl_multi_perform  
    89.       if(max_fd == -1) {  
    90.         sleep((unsigned int)curl_timeo / 1000);  
    91.       }  
    92.       else {  
    93.         T.tv_sec = curl_timeo/1000;  
    94.         T.tv_usec = (curl_timeo%1000)*1000;  
    95.   
    96.         /* 執行監聽,當文件描述符狀態發生改變的時候返回 
    97.          * 返回0,程序調用curl_multi_perform通知curl執行相應操做 
    98.          * 返回-1,表示select錯誤 
    99.          */  
    100.         if(0 > select(max_fd+1, &fd_read, &fd_write, &fd_except, &T)) {  
    101.           fprintf(stderr, "E: select(%i,,,,%li): %i: %s\n",  
    102.               max_fd+1, curl_timeo, errno, strerror(errno));  
    103.           return EXIT_FAILURE;  
    104.         }  
    105.       }  
    106.     }  
    107.   
    108.     while((msg = curl_multi_info_read(cm, &msgs_left))) {  
    109.       if(msg->msg == CURLMSG_DONE) {  
    110.         char *url;  
    111.         CURL *e = msg->easy_handle;  
    112.         curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, &url);  
    113.         fprintf(stderr, "R: %d - %s <%s>\n",  
    114.                 msg->data.result, curl_easy_strerror(msg->data.result), url);  
    115.         /*當一個easy handler傳輸完成,此easy handler仍然仍然停留在multi stack中, 
    116.         調用curl_multi_remove_handle將其從multi stack中移除,而後調用curl_easy_cleanup將其關閉*/  
    117.         curl_multi_remove_handle(cm, e);  
    118.         curl_easy_cleanup(e);  
    119.       }  
    120.       else {  
    121.         fprintf(stderr, "E: CURLMsg (%d)\n", msg->msg);  
    122.       }  
    123.     }  
    124.   }while(still_running);  
    125.   
    126.   //當multi stack中的全部傳輸都完成時,調用 curl_multi_cleanup關閉multi handler  
    127.   curl_multi_cleanup(cm);  
    128.   curl_global_cleanup();  
    129.   
    130.   return EXIT_SUCCESS;  
相關文章
相關標籤/搜索