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();
}
源码:
#include
#include
#include
#include
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;
}