
libcurl 多接口編程curl_multi_waitfds 提取文件描述符詳解【免費下載鏈接】curlA command line tool and library for transferring data with URL syntax, supporting DICT, FILE, FTP, FTPS, GOPHER, GOPHERS, HTTP, HTTPS, IMAP, IMAPS, LDAP, LDAPS, MQTT, MQTTS, POP3, POP3S, RTSP, SCP, SFTP, SMB, SMBS, SMTP, SMTPS, TELNET, TFTP, WS and WSS. libcurl offers a myriad of powerful features項目地址: https://gitcode.com/GitHub_Trending/cu/curl導(dǎo)讀curl_multi_waitfds是 libcurl 在 8.8.0 版本Added-in: 8.8.0見 curl_multi_waitfds.md中新增的多接口multi interface函數(shù)用于從CURLM句柄中提取與poll(2)的pollfd結(jié)構(gòu)相似的curl_waitfd描述符數(shù)組從而讓應(yīng)用可以按poll()的語義自行輪詢 libcurl 內(nèi)部使用的所有 socket。本文基于官方手冊 docs/libcurl/curl_multi_waitfds.md結(jié)合 lib/multi.c 的實現(xiàn)、include/curl/multi.h 的類型定義與 tests/libtest/lib2405.c 的測試用例完整講解該 API 的聲明、行為、錯誤處理與典型用法幫助你把它無縫接入自己的事件循環(huán)或 poll 驅(qū)動的 I/O 框架。1. 函數(shù)聲明與頭文件#include curl/curl.h #include stdlib.h CURLMcode curl_multi_waitfds(CURLM *multi, struct curl_waitfd *ufds, unsigned int size, unsigned int *fd_count);curl_multi_waitfds屬于 libcurl 多接口multi interface因此使用前需要先通過curl_multi_init()創(chuàng)建CURLM *句柄。函數(shù)原型在公共頭文件 include/curl/multi.h 中聲明并通過 lib/libcurl.defWindows 導(dǎo)出表與 projects/OS400/curl.inc.in 等平臺導(dǎo)出清單對外暴露所有協(xié)議Protocol: AllDICT、FILE、FTP、HTTP、HTTPS、MQTT、SCP、SFTP、SMTP、WS 等下的傳輸都適用。1.1 參數(shù)含義參數(shù)說明multi已初始化并掛載了 easy 句柄的CURLM *多句柄ufds由調(diào)用方提供的struct curl_waitfd數(shù)組libcurl 會向其中填充待輪詢的描述符數(shù)組容量由size指定sizeufds數(shù)組可容納的元素個數(shù)unsigned intfd_count輸出參數(shù)可為 NULL返回時寫入 multi 句柄當(dāng)前需要檢查可讀/可寫的描述符總數(shù)1.2 curl_waitfd 結(jié)構(gòu)與 pollfd 對齊的公共類型struct curl_waitfd在 include/curl/multi.h 中定義為/* Based on poll(2) structure and values. * We do not use pollfd and POLL* constants explicitly * to cover platforms without poll(). */ #define CURL_WAIT_POLLIN 0x0001 #define CURL_WAIT_POLLPRI 0x0002 #define CURL_WAIT_POLLOUT 0x0004 struct curl_waitfd { curl_socket_t fd; short events; short revents; };該結(jié)構(gòu)刻意與 POSIXpollfd保持同構(gòu)fd、events、revents三個字段事件掩碼語義也與POLLIN/POLLPRI/POLLOUT對應(yīng)但使用 libcurl 自己的常量CURL_WAIT_POLLIN/CURL_WAIT_POLLPRI/CURL_WAIT_POLLOUT這樣在不提供poll()的平臺如某些 Windows/Winsock 環(huán)境上也能編譯使用。頭文件注釋明確說明這是有意為之We do not use pollfd and POLL* constants explicitly to cover platforms without poll()。2. 行為說明2.1 功能定位該函數(shù)從給定的 multi 句柄中提取curl_waitfd結(jié)構(gòu)數(shù)組用于以與curl_multi_poll(3)相似的方式輪詢 multi 句柄的文件描述符。一旦其中某個描述符可讀或可寫就應(yīng)立即調(diào)用curl_multi_perform(3)驅(qū)動傳輸推進。它與 curl_multi_fdset 的關(guān)系是后者面向select()的fd_set模型而前者面向poll()的pollfd模型兩者底層都源自同一個內(nèi)部 pollset 收集結(jié)果見下文第 4 節(jié)。2.2 填充規(guī)則libcurl 會向調(diào)用方提供的ufds數(shù)組填充數(shù)據(jù)最多填充size個元素。若 multi 句柄實際使用的描述符數(shù)量大于sizelibcurl 返回CURLM_OUT_OF_MEMORY錯誤語義是緩沖區(qū)太小裝不下并非真實內(nèi)存耗盡。若fd_count非空返回時它指向的變量會保存 multi 句柄當(dāng)前需要檢查可讀/可寫的描述符總數(shù)用于讓調(diào)用方判斷數(shù)組是否夠大。調(diào)用方可以傳size等于 0 來先問個數(shù)此時 libcurl 只統(tǒng)計不填充fd_count會收到一個大于或等于實際描述符數(shù)量的值調(diào)用方據(jù)此分配足夠的存儲再在后續(xù)調(diào)用中傳入。官方手冊原文強調(diào)此時 fd_countreceives a number greater than or equal to the number of descriptors。2.3 錯誤處理函數(shù)返回CURLMcodeCURLM_OK (0)表示一切正常非零表示發(fā)生錯誤具體錯誤碼參見 libcurl-errors(3)。兩個典型的非零返回值CURLM_BAD_FUNCTION_ARGUMENT參數(shù)不合法。從 lib/multi.c 的實現(xiàn)可見當(dāng)!ufds (size || !fd_count)即傳入 NULL 的ufds卻同時給出非零size或 NULL 的fd_count導(dǎo)致無法返回計數(shù)時返回該錯誤。CURLM_OUT_OF_MEMORY傳入的數(shù)組容量size小于所需描述符數(shù)量。3. 官方示例兩步走動態(tài)分配curl_multi_waitfds.md 給出的完整示例展示了先問數(shù)量、再分配、再填充、再輪詢的標(biāo)準(zhǔn)用法#include stdlib.h int main(void) { CURLMcode mresult; struct curl_waitfd *ufds; CURLM *multi curl_multi_init(); do { /* call curl_multi_perform() */ /* get the count of file descriptors from the transfers */ unsigned int fd_count 0; mresult curl_multi_waitfds(multi, NULL, 0, fd_count); if(mresult ! CURLM_OK) { fprintf(stderr, curl_multi_waitfds() failed, code %d.\n, mresult); break; } if(!fd_count) continue; /* no descriptors yet */ /* allocate storage for our descriptors */ ufds malloc(fd_count * sizeof(struct curl_waitfd)); /* get wait descriptors from the transfers and put them into array. */ mresult curl_multi_waitfds(multi, ufds, fd_count, fd_count); if(mresult ! CURLM_OK) { fprintf(stderr, curl_multi_waitfds() failed, code %d.\n, mresult); free(ufds); break; } /* Do polling on descriptors in ufds */ free(ufds); } while(!mresult); }代碼中的關(guān)鍵步驟與注意事項第一次調(diào)用傳NULL與size 0僅通過fd_count獲取所需描述符數(shù)量若返回0說明 multi 句柄當(dāng)前沒有活動 socket例如句柄為空或傳輸尚未建立連接可continue繼續(xù)下一輪循環(huán)。動態(tài)分配數(shù)組malloc(fd_count * sizeof(struct curl_waitfd))容量剛好等于所需數(shù)量保證第二次調(diào)用不會觸發(fā)CURLM_OUT_OF_MEMORY。第二次調(diào)用填充數(shù)組ufds與fd_count同時傳入此時fd_count既是容量上限也是輸出參數(shù)返回后代表實際寫入的描述符個數(shù)與所需數(shù)量相等。輪詢與驅(qū)動對ufds中每個元素執(zhí)行poll()或epoll/kqueue等只要語義對齊CURL_WAIT_POLLIN/CURL_WAIT_POLLOUT一旦revents命中即調(diào)用curl_multi_perform()推進傳輸。循環(huán)退出條件!mresult只是示意真實應(yīng)用中一般會結(jié)合curl_multi_perform的剩余句柄數(shù)和curl_multi_info_read判斷傳輸是否全部完成。生產(chǎn)代碼還應(yīng)注意fd_count在兩次調(diào)用之間可能變化新連接建立、連接復(fù)用釋放等若第二次返回CURLM_OUT_OF_MEMORY應(yīng)重新分配后重試。4. 源碼實現(xiàn)從 pollset 到 curl_waitfd4.1 主流程lib/multi.ccurl_multi_waitfds的實現(xiàn)位于 lib/multi.c核心邏輯如下通過CURL_MAPI_ENTER/CURL_MAPI_LEAVE守衛(wèi)struct Curl_mapi_guard做并發(fā)保護保證多線程場景下 API 調(diào)用的安全性。參數(shù)校驗if(!ufds (size || !fd_count))返回CURLM_BAD_FUNCTION_ARGUMENT。初始化內(nèi)部struct easy_pollset ps與struct Curl_waitfds cwfds后者封裝用戶數(shù)組與計數(shù)定義見 lib/select.hwfds指向用戶數(shù)組、n為已填充數(shù)、count為容量。遍歷multi-process一個 uint32 位集合記錄待處理的 easy 句柄 ID對每個 easy 句柄調(diào)用Curl_multi_pollset(data, ps)收集其當(dāng)前 socket 及讀寫關(guān)注事件再通過Curl_waitfds_add_ps(cwfds, ps)增量寫入用戶數(shù)組并累加need所需描述符數(shù)。追加關(guān)閉連接Curl_cshutdn_add_waitfds相關(guān)的描述符見 lib/cshutdn.c。容量判斷if(need ! cwfds.n ufds)即所需數(shù)量超過已填充數(shù)量數(shù)組不夠裝時返回CURLM_OUT_OF_MEMORY。無論成敗fd_count都會被寫入need總需求數(shù)這正是傳size0可以問個數(shù)的實現(xiàn)基礎(chǔ)。4.2 事件映射lib/select.c描述符事件從內(nèi)部 pollset 到公共curl_waitfd的映射位于 lib/select.c 的Curl_waitfds_add_psfor(i 0; i ps-n; i) { short events 0; if(ps-actions[i] CURL_POLL_IN) events | CURL_WAIT_POLLIN; if(ps-actions[i] CURL_POLL_OUT) events | CURL_WAIT_POLLOUT; if(events) need cwfds_add_sock(cwfds, ps-sockets[i], events); }內(nèi)部CURL_POLL_IN/CURL_POLL_OUT動作被轉(zhuǎn)換為公共的CURL_WAIT_POLLIN/CURL_WAIT_POLLOUT位掩碼。值得注意的是cwfds_add_socklib/select.c會對重復(fù) socket 做事件合并if(sock cwfds-wfds[i].fd) { wfds[i].events | events; return 0; }即同一 socket 同時關(guān)注讀寫時只出現(xiàn)一個條目因此fd_count返回的是去重后的描述符數(shù)量。這一行為也被測試用例明確覆蓋見下節(jié)。另外可以推斷與curl_multi_fdsetlib/multi.c共享同一個Curl_multi_pollset收集機制因此兩種 API 拿到的 socket 集合是一致的只是輸出形式不同——fd_setselect 模型與curl_waitfd數(shù)組poll 模型。5. 測試用例驗證數(shù)量語義與錯誤路徑lib/multi.c 的配套測試 tests/libtest/lib2405.c數(shù)據(jù)文件為 tests/data/test2405另有同族測試 tests/data/test2407專門驗證curl_multi_waitfds的各種場景注釋中明確列出了預(yù)期場景預(yù)期描述符數(shù)空 multi 句柄0 個描述符HTTP/1 兩個傳輸無多路復(fù)用2 個描述符HTTP/2 兩個傳輸無多路復(fù)用2 個描述符HTTP/2 啟用多路復(fù)用CURLOPT_PIPEWAIT1 個描述符同一連接被合并去重同時測試還驗證了錯誤路徑非法參數(shù)如ufds與size組合不當(dāng)返回CURLM_BAD_FUNCTION_ARGUMENT傳入空ufds且size 0時返回所需描述符數(shù)量先問個數(shù)模式傳入非空ufds但容量小于需求時返回CURLM_OUT_OF_MEMORY且fd_count返回大于等于實際需求的數(shù)值所有由 multi 句柄驅(qū)動的傳輸最終都成功完成。其中HTTP/2 多路復(fù)用只有 1 個描述符的結(jié)果正好印證了第 4.2 節(jié)的去重合并邏輯——兩個 easy 句柄共享同一條 HTTP/2 連接時該連接的 socket 只被報告一次這正是curl_multi_waitfds相比簡單累加 fd 的fd_set方案在事件驅(qū)動編程中更精確的優(yōu)勢。6. 與 curl_multi_wait / curl_multi_poll / curl_multi_fdset 的關(guān)系curl_multi_waitfds官方手冊的 See-also 部分關(guān)聯(lián)了四個函數(shù)docs/libcurl/Makefile.inc 中亦有登記curl_multi_wait(3)/curl_multi_poll(3)這兩者在內(nèi)部完成收集 fd 阻塞輪詢 返回就緒數(shù)的全部工作調(diào)用方無需關(guān)心 fd 細節(jié)適合簡單場景curl_multi_waitfds則把收集 fd這一步暴露出來適合已有事件循環(huán)epoll/kqueue/io_uring需要自行注冊 socket 的架構(gòu)。curl_multi_perform(3)無論用哪種等待方式一旦描述符就緒都必須調(diào)用curl_multi_perform()實際讀寫數(shù)據(jù)、推進傳輸狀態(tài)機。curl_multi_fdset(3)select 模型的等價物返回三個fd_set與max_fdcurl_multi_waitfds是其 poll 模型對應(yīng)物二者選一即可具體見 curl_multi_fdset.md。三者選型建議自定義事件循環(huán)用curl_multi_waitfds拿 fd 自己管想省事直接阻塞等待就用curl_multi_poll歷史代碼基于 select 則保留curl_multi_fdset。7. 實戰(zhàn)要點小結(jié)先用size0問數(shù)量再分配數(shù)組curl_multi_waitfds(multi, NULL, 0, fd_count)返回需求數(shù)≥ 實際數(shù)據(jù)此malloc后再調(diào)用一次填充可避免CURLM_OUT_OF_MEMORY。注意fd_count是輸出參數(shù)第二次調(diào)用傳入的fd_count會被覆蓋為實際填充數(shù)別把舊的容量值留在別處使用。容量不足返回CURLM_OUT_OF_MEMORY且不保證部分填充的可用性應(yīng)重新分配更大數(shù)組后重試。事件語義用CURL_WAIT_POLLIN/CURL_WAIT_POLLOUT/CURL_WAIT_POLLPRI不要直接使用POLLIN等平臺常量前者在 include/curl/multi.h 中定義跨平臺一致。輪詢到就緒后立即調(diào)用curl_multi_perform()該函數(shù)是傳輸推進引擎輪詢只是叫醒機制。8.8.0 及以上版本才可用若需要兼容更早的 libcurl 版本請改用curl_multi_fdsetselect 模型或curl_multi_poll內(nèi)部封裝輪詢或在編譯期用版本宏做條件分支?!久赓M下載鏈接】curlA command line tool and library for transferring data with URL syntax, supporting DICT, FILE, FTP, FTPS, GOPHER, GOPHERS, HTTP, HTTPS, IMAP, IMAPS, LDAP, LDAPS, MQTT, MQTTS, POP3, POP3S, RTSP, SCP, SFTP, SMB, SMBS, SMTP, SMTPS, TELNET, TFTP, WS and WSS. libcurl offers a myriad of powerful features項目地址: https://gitcode.com/GitHub_Trending/cu/curl創(chuàng)作聲明:本文部分內(nèi)容由AI輔助生成(AIGC),僅供參考