一: multi與easy接口的不同處
The multi interface offers several abilities that the easy interface doesn't. They are mainly:
1. Enable a "pull" interface. The application that uses libcurl decides where and when to ask libcurl to get/send data.
2. Enable multiple simultaneous transfers in the same thread without making it complicated for the application.
3. Enable the application to wait for action on its own file descriptors and curl's file descriptors simultaneous easily.
4. Enable event-based handling and scaling transfers up to and beyond thousands of parallel connections.
在收發數據的時候能后取消操作。
在一個線程里同時多個傳輸操作而不造成混亂。
應用程序能夠同時等待它自己 和curl的文件描述符(句柄)。
能基於事件處理和傳輸超過數千個的並行鏈接。
二:
1,
CURLM *curl_multi_init( );
CURLMcode curl_multi_cleanup( CURLM *multi_handle );
With a multi handle and the multi interface you can do several simultaneous transfers in parallel.
Each single transfer is built up around an easy handle.
You create all the easy handles you need, and setup the appropriate options for each easy handle using curl_easy_setopt.
multi支持同時多文件並發傳輸。
單文件傳輸請用easy接口。
使用easy接口都需要setopt設置相應參數。
When an easy handle is setup and ready for transfer, then instead of using curl_easy_perform like when using the easy interface for transfers, you should add the easy handle to the multi handle with curl_multi_add_handle. You can add more easy handles to a multi handle at any point, even if other transfers are already running.
當設置好easy模式並准備傳輸的時候,可以使用curl_multi_add_handle替代curl_easy_perform。
能在任何時候增加一個esay模式句柄給multi模式,及時easy已經在執行傳輸操作了
Should you change your mind, the easy handle is again removed from the multi stack using curl_multi_remove_handle. Once removed from the multi handle, you can again use other easy interface functions like curl_easy_perform on the handle or whatever you think is necessary. You can remove handles at any point in time during transfers.
可以在任何時候從multi棧中移出,一旦移出可以再次使用curl_easy_perform。
Adding the easy handle to the multi handle does not start the transfer. Remember that one of the main ideas with this interface is to let your application drive. You drive the transfers by invoking curl_multi_perform. libcurl will then transfer data if there is anything available to transfer. It'll use the callbacks and everything else you have setup in the individual easy handles. It'll transfer data on all current transfers in the multi stack that are ready to transfer anything. It may be all, it may be none. When there's nothing more to do for now, it returns back to the calling application.
添加easy句柄到multi並不馬上開始執行,由curl_multi_perform啟動執行。
啟動后將執行所有multi stack中的收發事件。如果棧上是空的直接返回。
2,
CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles);
running_handles返回multi棧里仍需執行的句柄數。
If the amount of running_handles is changed from the previous call (or is less than the amount of easy handles you've added to the multi handle), you know that there is one or more transfers less "running". You can then call curl_multi_info_read to get information about each individual completed transfer, and that returned info includes CURLcode and more. If an added handle fails very quickly, it may never be counted as a running_handle.
如果running_handles的值比增加到multi棧時少了,就是有一些傳輸操作執行完了。你能調用curl_multi_info_read獲取每一個執行完成的操作信息。如果添加立即失敗,它不會被算入running_handles。
/* call curl_multi_perform or curl_multi_socket_action first, then loop
through and check if there are any transfers that have completed */
struct CURLMsg *m;
do {
int msgq = 0;
m = curl_multi_info_read(multi_handle, &msgq);
if(m && (m->msg == CURLMSG_DONE)) {
CURL *e = m->easy_handle;
transfers--;
curl_multi_remove_handle(multi_handle, e);
curl_easy_cleanup(e);
}
} while(m);
3,
Your application extracts info from libcurl about when it would like to get invoked to transfer data or do other work. The most convenient way is to use curl_multi_wait that will help you wait until the application should call libcurl again. The older API to accomplish the same thing is curl_multi_fdset that extracts fd_sets from libcurl to use in select() or poll() calls in order to get to know when the transfers in the multi stack might need attention. Both these APIs allow for your program to wait for input on your own private file descriptors at the same time curl_multi_timeout also helps you with providing a suitable timeout period for your select() calls.
你的程序可以在libcurl正工作的時候獲取一些信息。
通常使用curl_multi_wait等待直到線程被libcurl喚起。老的api使用curl_multi_fdset設置 select或者poll模型觸發。
等待這些api返回的同時可以使用curl_multi_timeout獲取一個為select設置的超時時間。
curl_multi_wait polls all file descriptors used by the curl easy handles contained in the given multi handle set. It will block until activity is detected on at least one of the handles or timeout_ms has passed. Alternatively, if the multi handle has a pending internal timeout that has a shorter expiry time than timeout_ms, that shorter time will be used instead to make sure timeout accuracy is reasonably kept.
The calling application may pass additional curl_waitfd structures which are similar to poll(2)'s pollfd structure to be waited on in the same call.
On completion, if numfds is non-NULL, it will be populated with the total number of file descriptors on which interesting events occurred. This number can include both libcurl internal descriptors as well as descriptors provided in extra_fds
curl_multi_wait輪詢multi上的所有easy句柄,一直阻塞直到至少有一個被觸發或者超時。
如果multi句柄正因為網絡延時而掛起,會有一個更短更精確的時間來代替我們自己設置的超時時間timeout_ms。
curl_waitfd數組怎加需要監聽的socket。
wait返回后,numfds講返回被觸發的事件數量,若為0表示超時或者沒有事件等待。numfds的值包括multi棧上的和extra_fds新加的之和。
CURLMcode curl_multi_wait(CURLM *multi_handle,
struct curl_waitfd extra_fds[],
unsigned int extra_nfds,
int timeout_ms,
int *numfds);
CURLMcode curl_multi_timeout(CURLM *multi_handle, long *timeout);
An application using the libcurl multi interface should call curl_multi_timeout to figure out how long it should wait for socket actions - at most - before proceeding.
一個使用了multi接口的程序應該用url_multi_timeout來預估出在程序激活前最少應該等在socket事件多長時間。
The timeout value returned in the long timeout points to, is in number of milliseconds at this very moment. If 0, it means you should proceed immediately without waiting for anything. If it returns -1, there's no timeout at all set.
第二個指針參數timeout獲取毫秒的超時等待時間,如果為0表示沒有等待繼續做一些事情,如果為-1表示所有都沒設置超時時間。
struct timeval timeout;
long timeo;
curl_multi_timeout(multi_handle, &timeo);
if(timeo < 0)
timeo = 980;// no set timeout, use a default
timeout.tv_sec = timeo / 1000;
timeout.tv_usec = (timeo % 1000) * 1000;
select(maxfd+1, &fdread, &fdwrite, &fdexcep, &timeout);// wait for activities no longer than the set timeout
CURLMcode curl_multi_fdset(CURLM *multi_handle,
fd_set *read_fd_set,
fd_set *write_fd_set,
fd_set *exc_fd_set,
int *max_fd);
獲取可以讀寫或有錯誤的集合。
If no file descriptors are set by libcurl, max_fd will contain -1 when this function returns. Otherwise it will contain the highest descriptor number libcurl set. When libcurl returns -1 in max_fd, it is because libcurl currently does something that isn't possible for your application to monitor with a socket and unfortunately you can then not know exactly when the current action is completed using select(). You then need to wait a while before you proceed and call curl_multi_perform anyway. How long to wait? We suggest 100 milliseconds at least, but you may want to test it out in your own particular conditions to find a suitable value.
如果sd_set都沒被libcurl設置,這函數返回時max_fd值為-1。否則,max_fd為fd_set里被觸發的最大個數。
如果max_fd為-1可能是應為程序正在監聽一個socket的時候libcurl當前什么也不能不了;並且很不幸此時你不能知道select何時完事。因此執行curl_multi_perform前需要等待一段時間,建議100毫秒,但是可以根據自己的特殊環境嘗試設置一個合適的等待時間。
三:
1,
multi是依賴easy接口的。
2,
步湊:
curl_multi _init初始化一個multi curl對象,
為了同時進行多個curl的並發訪問,需要初始化多個easy curl對象,使用curl_easy_setopt進行相關設置,
然后調用curl_multi _add_handle把easy curl對象添加到multi curl對象中,
添加完畢后執行curl_multi_perform方法進行並發的訪問,
訪問結束后curl_multi_remove_handle移除相關easy curl對象,
curl_easy_cleanup清除easy curl對象,
最后curl_multi_cleanup清除multi curl對象。
3,
#include <stdio.h> #include <string.h> #include <curl/curl.h> /* This is a simple example showing how to fetch mail using libcurl's IMAP * capabilities. It builds on the imap-fetch.c example to demonstrate how to * use libcurl's multi interface. * * Note that this example requires libcurl 7.30.0 or above. */ #define MULTI_PERFORM_HANG_TIMEOUT 60 * 1000 static struct timeval tvnow(void) { struct timeval now; /* time() returns the value of time in seconds since the epoch */ now.tv_sec = (long)time(NULL); now.tv_usec = 0; return now; } static long tvdiff(struct timeval newer, struct timeval older) { return (newer.tv_sec - older.tv_sec) * 1000 + (newer.tv_usec - older.tv_usec) / 1000; } int main(void) { CURL *curl; CURLM *mcurl; int still_running = 1; struct timeval mp_start; curl_global_init(CURL_GLOBAL_DEFAULT); curl = curl_easy_init() if (!curl) return 1; mcurl = curl_multi_init(); if (!mcurl) return 2; /* Set username and password */ curl_easy_setopt(curl, CURLOPT_USERNAME, "user"); curl_easy_setopt(curl, CURLOPT_PASSWORD, "secret"); /* This will fetch message 1 from the user's inbox */ curl_easy_setopt(curl, CURLOPT_URL, "imap://imap.example.com/INBOX/;UID=1"); /* Tell the multi stack about our easy handle */ curl_multi_add_handle(mcurl, curl); /* Record the start time which we can use later */ mp_start = tvnow(); /* We start some action by calling perform right away */ curl_multi_perform(mcurl, &still_running); while (still_running) { struct timeval timeout; fd_set fdread; fd_set fdwrite; fd_set fdexcep; int maxfd = -1; int rc; CURLMcode mc; /* curl_multi_fdset() return code */ long curl_timeo = -1; /* Initialise the file descriptors */ FD_ZERO(&fdread); FD_ZERO(&fdwrite); FD_ZERO(&fdexcep); /* Set a suitable timeout to play around with */ timeout.tv_sec = 1; timeout.tv_usec = 0; curl_multi_timeout(mcurl, &curl_timeo); if (curl_timeo >= 0) { timeout.tv_sec = curl_timeo / 1000; if (timeout.tv_sec > 1) timeout.tv_sec = 1; else timeout.tv_usec = (curl_timeo % 1000) * 1000; } /* get file descriptors from the transfers */ mc = curl_multi_fdset(mcurl, &fdread, &fdwrite, &fdexcep, &maxfd); if (mc != CURLM_OK) { fprintf(stderr, "curl_multi_fdset() failed, code %d.\n", mc); break; } /* On success the value of maxfd is guaranteed to be >= -1. We call select(maxfd + 1, ...); specially in case of (maxfd == -1) there are no fds ready yet so we call select(0, ...) --or Sleep() on Windows-- to sleep 100ms, which is the minimum suggested value in the curl_multi_fdset() doc. */ if (maxfd == -1) { #ifdef _WIN32 Sleep(100); rc = 0; #else /* Portable sleep for platforms other than Windows. */ struct timeval wait = { 0, 100 * 1000 }; /* 100ms */ rc = select(0, NULL, NULL, NULL, &wait); #endif } else { /* Note that on some platforms 'timeout' may be modified by select(). If you need access to the original value save a copy beforehand. */ rc = select(maxfd + 1, &fdread, &fdwrite, &fdexcep, &timeout); } if (tvdiff(tvnow(), mp_start) > MULTI_PERFORM_HANG_TIMEOUT) { fprintf(stderr, "ABORTING: Since it seems that we would have run forever.\n"); break; } switch (rc) { case -1: /* select error */ break; case 0: /* timeout */ default: /* action */ curl_multi_perform(mcurl, &still_running); break; } } /* Always cleanup */ curl_multi_remove_handle(mcurl, curl); curl_multi_cleanup(mcurl); curl_easy_cleanup(curl); curl_global_cleanup(); return 0; }
