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。
源码:
#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 "); return EXIT_FAILURE; } if(!curl_multi_timeout(cm, &curl_timeo)) { fprintf(stderr, "E: curl_multi_timeout "); 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 ", 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> ", 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) ", msg->msg); } } }while(still_running); //当multi stack中的所有传输都完成时,调用 curl_multi_cleanup关闭multi handler curl_multi_cleanup(cm); curl_global_cleanup(); return EXIT_SUCCESS; }
声明:该文观点仅代表作者本人,入门客AI创业平台信息发布平台仅提供信息存储空间服务,如有疑问请联系rumenke@qq.com。