#include <errno.h> #include <stdlib.h> #include <string.h> #ifndef WIN32 #include <unistd.h> #endif #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", "http://www.iana.org", "http://www.amazon.com", "http://www.netcraft.com", "http://www.heise.de", "http://www.chip.de", "http://www.ca.com", "http://www.cnet.com", "http://www.news.com", "http://www.cnn.com", "http://www.wikipedia.org", "http://www.dell.com", "http://www.hp.com", "http://www.cert.org", "http://www.mit.edu", "http://www.nist.gov" }; #define MAX 10 #define CNT sizeof(urls)/sizeof(char*) 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; } int nIndex=0; 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_getinfo(msg->easy_handle, CURLINFO_PRIVATE, &url);查询出来 curl_easy_setopt(eh, CURLOPT_VERBOSE, 0L); //如果你想CURL报告每一件意外的事情,设置这个选项为一个非零值 curl_easy_setopt(eh, CURLOPT_WRITEDATA, (void *)&nIndex); //设置调用cb函数时传递cb函数的第四个参数 curl_easy_setopt(eh, CURLOPT_USERAGENT, "Lavf/55.13.102"); //设置useragent curl_multi_add_handle(cm, eh); } int main(void) { CURLM *cm; CURLMsg *msg; long L; unsigned int C=0; int M, Q, U = -1; fd_set R, W, E; struct timeval T; curl_global_init(CURL_GLOBAL_ALL); cm = curl_multi_init(); /* we can optionally limit the total amount of connections this multi handle uses */ curl_multi_setopt(cm, CURLMOPT_MAXCONNECTS, (long)MAX); for(C = 0; C < MAX; ++C) { init(cm, C); } while(U) { curl_multi_perform(cm, &U); //执行并发请求,非阻塞,立即返回 if(U) { FD_ZERO(&R); FD_ZERO(&W); FD_ZERO(&E); //获取cm需要监听的文件描述符集合,如果返回的M等于-1,需要等一会然后再次调用curl_multi_perform,等多久?建议至少100毫秒,但你可能想在你自己的特定条件下测试它,找到一个合适的值 if(curl_multi_fdset(cm, &R, &W, &E, &M)) { fprintf(stderr, "E: curl_multi_fdset "); return EXIT_FAILURE; } if(curl_multi_timeout(cm, &L)) { fprintf(stderr, "E: curl_multi_timeout "); return EXIT_FAILURE; } if(L == -1) L = 100; if(M == -1) { #ifdef WIN32 Sleep(L); #else sleep((unsigned int)L / 1000); #endif } else { T.tv_sec = L/1000; T.tv_usec = (L%1000)*1000; /*确定一个或多个套接口的状态,可查询它的可读性、可写性及错误状态信息。 返回值: select()调用返回处于就绪状态并且已经包含在fd_set结构中的描述字总数;如果超时则返回0;否则的话,返回SOCKET_ERROR(-1)错误,应用程序可通过WSAGetLastError()获取相应错误代码。 */ if(0 > select(M+1, &R, &W, &E, &T)) { fprintf(stderr, "E: select(%i,,,,%li): %i: %s ",M+1, L, errno, strerror(errno)); return EXIT_FAILURE; } } } /*获取当前解析的cURL的相关传输信息 查询批处理句柄是否单独的传输线程中有消息或信息返回。消息可能包含诸如从单独的传输线程返回的错误码或者只是传输线程有没有完成之类的报告。 重复调用这个函数,它每次都会返回一个新的结果,直到这时没有更多信息返回时,FALSE 被当作一个信号返回。通过msgs_in_queue返回的整数指出将会包含当这次函数被调用后,还剩余的消息数。 Warning 返回的资源指向的数据调用curl_multi_remove_handle()后将不会存在。 */ while((msg = curl_multi_info_read(cm, &Q))) { 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); curl_multi_remove_handle(cm, e); curl_easy_cleanup(e); } else { fprintf(stderr, "E: CURLMsg (%d) ", msg->msg); } if(C < CNT) { init(cm, C++); U++; /* just to prevent it from remaining at 0 if there are more URLs to get */ } } } curl_multi_cleanup(cm); curl_global_cleanup(); return EXIT_SUCCESS; }