c/c++调用libcurl库发送http请求的两种基本用法

libcurl主要提供了两种发送http请求的方式,分别是Easy interface方式和multi interface方式,前者是采用阻塞的方式发送单条数据,后者采用组合的方式可以一次性发送多条数据

一、Easy interface

libcurl的easy interface是最基本的用法,简要流程为:
1、在主线程中调用curl_global_init(CURL_GLOBAL_ALL)初始化
2、调用curl_easy_init获取一个句柄;
3、调用curl_easy_setopt函数设置此次传输的一些基本参数,如url地址、http头、cookie信息、发送超时时间等,其中,CURLOPT_URL是必设的选项;
4、设置完成后,调用curl_easy_perform函数发送数据;
5、数据发送完毕后,调用curl_easy_cleanup清空句柄;
6、调用curl_global_cleanup()做清理工作。

实现代码:

bool send_easy_hanler(char* post_url, req_t* req) 
    {  
      //easy handler的句柄  
      CURL* curl = NULL;  
      CURLcode res = CURLE_OK;  
      //HTTP报文头  
      struct curl_slist* headers = NULL;  
      char tmp_str[256] = { 0 };  
      
      //构建HTTP报文头  
      snprintf(tmp_str, sizeof(tmp_str), "User-Agent: %s", req->user_agent_);  
      headers = curl_slist_append(headers, tmp_str);  
      snprintf(tmp_str, sizeof(tmp_str), "Accept-Language: %s", req->language_);  
      headers = curl_slist_append(headers, tmp_str);  
      snprintf(tmp_str, sizeof(tmp_str), "X-FORWORDED-FOR: %s", req->ip_.c_str());  
      headers = curl_slist_append(headers, tmp_str);  
      
      /*这个函数只能用一次,如果这个函数在curl_easy_init函数调用时还没调用, 
      它讲由libcurl库自动调用,所以多线程下最好在主线程中调用一次该函数以防止在线程 
      中curl_easy_init时多次调用*/  
      curl_global_init(CURL_GLOBAL_ALL);  
        
      //初始化easy handler句柄  
      curl = curl_easy_init();  
      if (curl) {  
        //设置post请求的url地址  
        curl_easy_setopt(curl, CURLOPT_URL, post_url);  
        //设置HTTP头  
        curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);  
        //设置发送超时时间  
        curl_easy_setopt(curl, CURLOPT_TIMEOUT, 1);  
      
        //执行单条请求  
        res = curl_easy_perform(curl);  
        if (res != CURLE_OK) {  
          //curl_easy_strerror进行出错打印  
          LOG(WARNING) << "curl_easy_perform() failed:" << curl_easy_strerror(res);  
        }  
          
        curl_slist_free_all(headers);  
          
        //这个调用用来结束一个会话.与curl_easy_init配合着用  
        curl_easy_cleanup(curl);  
          
        //在结束libcurl使用的时候,用来对curl_global_init做的工作清理。类似于close的函数  
        curl_global_cleanup();  
    }  


二、multi interface

multi interface提供了多种easy interface没有的特性,主要是
1、提供了pull接口,使用libcurl的程序能够决定何时何处调用libcurl来get/send数据
2、在同一线程中实现多条数据同时发送,且并没有使得程序更加复杂
3、程序可以在自己的文件描述符和curl的文件描述符中同时等待执行
4、提供基于事件的处理、扩大传输规模到数千个并发连接

multi接口的使用会比easy 接口稍微复杂点,毕竟multi接口是依赖easy接口的,简要流程为:
1、使用curl_multi_init创建一个multi handle,这个handler会在后续的curl_multi_*函数中使用multi handler可以同时并发传输多条数据,每一条单独的数据是由一个easy handler创建;
2、需要事先将需要传输的所有easyhandler创建好,并使用curl_easy_setopt设置各自属性,接着调用curl_multi_add_handle函数逐个添加到multi handle中;
3、调用curl_multi_perform进程数据传输,传输过程中将会调用每一个easy handler设置的回调函数或者配置内容,程序通过函数curl_multi_fdset、select()提取信息来判断何时进行数据传输等操作,函数curl_multi_perform的一个输入参数储存仍在进行传输的数据量,通过读取该变量,可以判断multi handles是否运行完毕,传输完毕不代表传输成功,可能有一个或多个传输失败;
4、调用函数curl_multi_info_read可以获取当前或之前传输的信息,重复调用该函数直到该消息队列为空,每一条返回信息都包含对应的easl handler的传输情况;
5、当一个easy handler传输完成,此easy handler仍然仍然停留在multi stack中,需要调用curl_multi_remove_handle将其从multi stack中移除,然后调用curl_easy_cleanup将其关闭;
6、当multi stack中的所有传输都完成时,调用 curl_multi_cleanup关闭multi handler,需要注意的是事先要调用curl_easy_cleanup逐个清空所有easy handler。

源码:

[cpp] view plain copy 在CODE上查看代码片派生到我的代码片

    #include <errno.h>  
    #include <stdlib.h>  
    #include <string.h>  
    #include <curl/multi.h>  
      
    static const char *urls[] = {  
      "http://www.microsoft.com",  
      "http://www.opensource.org",  
      "http://www.google.com",  
      "http://www.yahoo.com",  
      "http://www.ibm.com",  
      "http://www.mysql.com",  
      "http://www.oracle.com",  
      "http://www.ripe.net",  
    };  
      
    #define MAX 8 /* number of simultaneous transfers */  
    #define CNT sizeof(urls)/sizeof(char*) /* total number of transfers to do */  
      
    /*此函数读取libcurl发送数据后的返回信息,如果不设置此函数, 
    那么返回值将会输出到控制台,影响程序性能*/  
    static size_t cb(char *d, size_t n, size_t l, void *p)  
    {  
      /* take care of the data here, ignored in this example */  
      (void)d;  
      (void)p;  
      return n*l;  
    }  
      
    //设置单个easy handler的属性添加单个easy handler到multi handler中,  
    static void init(CURLM *cm, int i)  
    {  
      CURL *eh = curl_easy_init();  
      
      curl_easy_setopt(eh, CURLOPT_WRITEFUNCTION, cb);  
      curl_easy_setopt(eh, CURLOPT_HEADER, 0L);  
      curl_easy_setopt(eh, CURLOPT_URL, urls[i]);  
      curl_easy_setopt(eh, CURLOPT_PRIVATE, urls[i]);  
      curl_easy_setopt(eh, CURLOPT_VERBOSE, 0L);  
      
      //添加easy handler 到multi handler中  
      curl_multi_add_handle(cm, eh);  
    }  
      
    int main(void)  
    {  
      CURLM *cm;  
      CURLMsg *msg;  
      long curl_timeo;  
      unsigned int C=0;  
      int max_fd, msgs_left, still_running = -1;//still_running判断multi handler是否传输完毕  
      fd_set fd_read, fd_write, fd_except;  
      struct timeval T;  
      
      curl_global_init(CURL_GLOBAL_ALL);  
      
      cm = curl_multi_init();  
      
      //现在multi handler的最大连接数  
      curl_multi_setopt(cm, CURLMOPT_MAXCONNECTS, (long)MAX);  
      
      for(C = 0; C < MAX; ++C) {  
        init(cm, C);  
      }  
      
        
      do{  
        curl_multi_perform(cm, &still_running);  
      
        if(still_running) {  
          FD_ZERO(&fd_read);  
          FD_ZERO(&fd_write);  
          FD_ZERO(&fd_except);  
      
          //获取multi curl需要监听的文件描述符集合 fd_set  
          if(!curl_multi_fdset(cm, &fd_read, &fd_write, &fd_except, &max_fd)) {  
            fprintf(stderr, "E: curl_multi_fdset\n");  
            return EXIT_FAILURE;  
          }  
      
          if(!curl_multi_timeout(cm, &curl_timeo)) {  
            fprintf(stderr, "E: curl_multi_timeout\n");  
            return EXIT_FAILURE;  
          }  
          if(curl_timeo == -1)  
            curl_timeo = 100;  
      
          //如果max_fd返回-1,休眠一段时间后继续执行curl_multi_perform  
          if(max_fd == -1) {  
            sleep((unsigned int)curl_timeo / 1000);  
          }  
          else {  
            T.tv_sec = curl_timeo/1000;  
            T.tv_usec = (curl_timeo%1000)*1000;  
      
            /* 执行监听,当文件描述符状态发生改变的时候返回 
             * 返回0,程序调用curl_multi_perform通知curl执行相应操作 
             * 返回-1,表示select错误 
             */  
            if(0 > select(max_fd+1, &fd_read, &fd_write, &fd_except, &T)) {  
              fprintf(stderr, "E: select(%i,,,,%li): %i: %s\n",  
                  max_fd+1, curl_timeo, errno, strerror(errno));  
              return EXIT_FAILURE;  
            }  
          }  
        }  
      
        while((msg = curl_multi_info_read(cm, &msgs_left))) {  
          if(msg->msg == CURLMSG_DONE) {  
            char *url;  
            CURL *e = msg->easy_handle;  
            curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, &url);  
            fprintf(stderr, "R: %d - %s <%s>\n",  
                    msg->data.result, curl_easy_strerror(msg->data.result), url);  
            /*当一个easy handler传输完成,此easy handler仍然仍然停留在multi stack中, 
            调用curl_multi_remove_handle将其从multi stack中移除,然后调用curl_easy_cleanup将其关闭*/  
            curl_multi_remove_handle(cm, e);  
            curl_easy_cleanup(e);  
          }  
          else {  
            fprintf(stderr, "E: CURLMsg (%d)\n", msg->msg);  
          }  
        }  
      }while(still_running);  
      
      //当multi stack中的所有传输都完成时,调用 curl_multi_cleanup关闭multi handler  
      curl_multi_cleanup(cm);  
      curl_global_cleanup();  
      
      return EXIT_SUCCESS; 
    }  



libcurl里主要用到的函数有curl_easy_init(),curl_easy_setopt()和curl_easy_perform().
顾名思义,curl_easy_init()是用来初始化libcurl库的(不知道对不对,就这么理解吧),
curl_easy_setopt()是用来设置各种curl参数的,setopt就是setoption,
curl_easy_perform()是用来执行curl的各种操作的,并且通过分析其返回值,可以判断curl执行的情况。
1)curl_easy_setopt函数介绍

本节主要介绍curl_easy_setopt中跟http相关的参数。注意本节的阐述都是以libcurl作为主体,其它为客体来阐述的。
1.CURLOPT_URL
设置访问URL
2.CURLOPT_WRITEFUNCTION,CURLOPT_WRITEDATA
通过设定一个回调函数,执行libcurl在接受到数据后用户想进行的操作,通常函数多做数据保存的功能,如处理下载文件。CURLOPT_WRITEDATA用于表明CURLOPT_WRITEFUNCTION函数中的stream指针的来源,说白了就是设定回调函数的第四个参数的数据类型。回调函数原型为:size_tfunction( void *ptr, size_t size, size_t nmemb, void*stream);
3.CURLOPT_HEADERFUNCTION,CURLOPT_HEADERDATA
通过设定一个回调函数,执行libcurl在接受到http头数据后用户想进行的操作。CURLOPT_WRITEDATA传递指针给libcurl,该指针表明CURLOPT_HEADERFUNCTION 函数的stream指针的来源。回调函数原型为size_t function( void *ptr, size_t size,size_t nmemb, void*stream);
4.CURLOPT_READFUNCTION CURLOPT_READDATA
libCurl需要读取数据传递给远程主机时将调用CURLOPT_READFUNCTION指定的函数,函数原型是:size_tfunction(void *ptr, size_t size, size_t nmemb,void *stream).CURLOPT_READDATA表明CURLOPT_READFUNCTION函数原型中的stream指针来源,说白了就是设定回调函数的第四个参数的数据类型。
5.CURLOPT_NOPROGRESS,CURLOPT_PROGRESSFUNCTION,CURLOPT_PROGRESSDATA
跟数据传输进度相关的参数。CURLOPT_PROGRESSFUNCTION指定的函数正常情况下每秒被libcurl调用一次,为了使CURLOPT_PROGRESSFUNCTION被调用,CURLOPT_NOPROGRESS必须被设置为false,CURLOPT_PROGRESSDATA指定的参数将作为CURLOPT_PROGRESSFUNCTION指定函数的第一个参数
6.CURLOPT_TIMEOUT,CURLOPT_CONNECTIONTIMEOUT:
CURLOPT_TIMEOUT 用于设置传输时间,CURLOPT_CONNECTIONTIMEOUT 设置连接等待时间
7.CURLOPT_FOLLOWLOCATION
设置重定位URL
CURLOPT_RANGE: CURLOPT_RESUME_FROM:
断点续传相关设置。CURLOPT_RANGE 指定char*参数传递给libcurl,用于指明http域的RANGE头域,例如:
表示头500个字节:bytes=0-499
表示第二个500字节:bytes=500-999
表示最后500个字节:bytes=-500
表示500字节以后的范围:bytes=500-
第一个和最后一个字节:bytes=0-0,-1
同时指定几个范围:bytes=500-600,601-999
   CURLOPT_RESUME_FROM 传递一个long参数给libcurl,指定你希望开始传递的偏移量。
8.CURLOPT_UPLOAD:
如果第三个参数被设置为1的话,就是让libcurl做好上传的准备。如果传输协议是http的话,uoload就是发送put。
9. CURLOPT_SSL_VERIFYPEER:
第三个参数的缺省值为1.该函数多用于设定curl忽略对网站证书的检查(不管忽略不忽略,curl都是检查的)。
10.CURLOPT_VERBOSE
相当厉害的一个参数,可以向控制台(默认)输出curl接受和发送的数据,输出流可以重定向。
11.CURLOPT_HTTPGET
将curl向服务器交互数据的方式改变为get

2)curl_easy_perform 函数说明(error状态码)

该函数完成curl_easy_setopt指定的所有选项,本节重点介绍curl_easy_perform的返回值。返回0意味一切ok,非0代表错误发生。主要错误码说明:
1.CURLE_OK
任务完成一切都好
2.CURLE_UNSUPPORTED_PROTOCOL
不支持的协议,由URL的头部指定
3.CURLE_COULDNT_CONNECT
不能连接到remote 主机或者代理
4.CURLE_REMOTE_ACCESS_DENIED
访问被拒绝
5.CURLE_HTTP_RETURNED_ERROR
Http返回错误
6.CURLE_READ_ERROR
读本地文件错误

3)curl_easy_getinfo函数说明

1.CURLINFO_RESPONSE_CODE
获得http返回的状态码,如400,200


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值