[转] libcurl异步方式使用总结(附流程图)
文為轉載,原文地址:libcurl異步方式使用總結
實習期間用到了libcurl來做HTTPS雙向認證,用的是異步方式,簡單總結一下。
libcurl這個庫的同步方式很簡單,不做介紹,而異步方式很難理解,本博客參考官網(wǎng)的demo講解,剛開始看可能很蒙,最后會整合全流程。
使用步驟如下:
1.初始化創(chuàng)建一個multi句柄:
1 CURLM *multi = curl_multi_init();
2.對multi句柄設置socket回調和timer回調:
1 curl_multi_setopt(multi, CURLMOPT_SOCKETFUNCTION, multi_sock_cb); 2 curl_multi_setopt(multi, CURLMOPT_SOCKETDATA, ¶m); 3 curl_multi_setopt(multi, CURLMOPT_TIMERFUNCTION, multi_timer_cb); 4 curl_multi_setopt(multi, CURLMOPT_TIMERDATA, ¶m);
3.對multi句柄添加easy句柄,異步開始:
1 CURL *easy = curl_easy_init(); 2 curl_easy_setopt(conn->easy, CURLOPT_URL, url); 3 curl_easy_setopt(conn->easy, CURLOPT_WRITEFUNCTION, write_cb); // 負責讀入數(shù)據(jù)的函數(shù) 4 curl_easy_setopt(conn->easy, CURLOPT_WRITEDATA, &data); 5 curl_multi_add_handle(multi, easy);
先看看第三行設置的write_cb,該函數(shù)是你讀入數(shù)據(jù)的函數(shù):
1 /*
2 * ptr 指向libcurl庫讀到的數(shù)據(jù)
3 * data 用戶自定義的緩沖區(qū), 上面第四行設置
4 */
5 size_t write_cb(void *ptr, size_t size, size_t nmemb, void *data) {
6 // 把ptr指向的數(shù)據(jù)拷到data
7 }
在curl_multi_add_handle運行結束的那一刻,第2步設置的multi_timer_cb馬上被拉起執(zhí)行,讓我們看看multi_timer_cb的函數(shù)聲明:
1 /* 2 * multi 第一步創(chuàng)建的句柄 3 * timeout_ms libcurl庫維護的一個超時時間,具體怎么算不清楚,回調時會自動賦值 4 * param 第二步設置的參數(shù) 5 * return 錯誤碼 6 */ 7 int multi_timer_cb(CURLM *multi, long timeout_ms, void *param)
libcurl庫本身沒有定時器功能,只是告訴你一個定時時間timeout_ms,這就要求我們自己維護一個定時器和到期的回調函數(shù)timer_cb。
偽代碼表示如下:
1 int multi_timer_cb(CURLM *multi, long timeout_ms, void *param) {
2 timer_.add(timer_cb, ms); // ms后執(zhí)行timer_cb
3 }
timer_cb主要調用libcurl的兩個函數(shù):
1 void timer_cb(param...) {
2 CURLMcode rc;
3 rc = curl_multi_socket_action(multi, CURL_SOCKET_TIMEOUT, 0,
4 &still_running);
5 while((msg = curl_multi_info_read(multi, &msgs_left))) { // 判斷數(shù)據(jù)是否讀完
6 if(msg->msg == CURLMSG_DONE) {
7 // 清理資源操作
8 }
9 }
10 }
而multi_sock_cb類似如此:
1 /* 2 * e 第三步添加的easy句柄 3 * s libcurl創(chuàng)建維護的socket 4 * what 執(zhí)行動作(讀或寫) 5 */ 6 int multi_sock_cb(CURL *e, curl_socket_t s, int what, void *cbp, void *sockp)
在libcurl維護的socket描述符發(fā)生狀態(tài)改變時(變回可讀或可寫),multi_sock_cb才會被回調。注意,函數(shù)回調時,第二個參數(shù)是socket描述符,這是libcurl維護創(chuàng)建的,但是你把它添加到poller(代指epoll或poll的封裝類)或者libev等事件觸發(fā)器中去,并設置回調函數(shù),偽代碼如下
1 int multi_sock_cb(CURL *e, curl_socket_t s, int what, void *cbp, void *sockp) {
2 poller.add(s, socket_cb); // 當描述符可讀和可寫時,調用socket_cb
3 }
看到這里是不是懵逼,不要急,最后會講解全流程。socket_cb里也是調用兩個libcurl函數(shù):
1 void socket_cb(param...) {
2 CURLMcode rc;
3 rc = curl_multi_socket_action(multi, CURL_SOCKET_TIMEOUT, 0,
4 &still_running);
5 while((msg = curl_multi_info_read(multi, &msgs_left))) { // 判斷數(shù)據(jù)是否讀完
6 if(msg->msg == CURLMSG_DONE) {
7 // 清理資源操作
8 }
9 }
10 }
好了,函數(shù)寫成這樣就差不多了(都是偽代碼,具體用法還是看demo)。那么這代碼到底是怎么執(zhí)行的呢,請看下圖。
1、在curl_multi_add_handle之后,multi_timer_cb會馬上被拉起調用,然后第一次調用的話timeout是0ms,所以timer_cb也會被拉起,然后調用curl_multi_socket_action。
2、此時,請注意在curl_multi_add_handle之前已經(jīng)設置過了url了,所以此時是需要發(fā)起http請求,即寫請求,所以在curl_multi_socket_action中l(wèi)ibcurl會創(chuàng)建一個socket描述符,然后狀態(tài)變?yōu)榭蓪憽?/p>
3、此時,因為libcurl的socket描述符狀態(tài)發(fā)生改變,所以multi_sock_cb會被拉起,multi_sock_cb中就把socket描述符添加到poller中,設置寫事件的回調函數(shù)為socket_cb。
4、因為socket描述符是可寫的,所以poller會調用sock_cb,curl_multi_socket_action又被調用,而此函數(shù)就會發(fā)送http請求(即libcurl負責寫fd)。
5、等到http請求被發(fā)送完,就需要接收響應,所以libcurl會把socket描述符從寫狀態(tài)改為讀狀態(tài)。
6、因為socket描述符變?yōu)榭勺x,狀態(tài)改變,multi_sock_cb又被調用,此時在poller中,將socket描述符的讀事件回調函數(shù)設置為socket_cb。
7、當響應到來的時候,socket描述符可讀,調用socket_cb,從而調用curl_multi_socket_action,該函數(shù)就就會異步調用之前設置的、負責讀入數(shù)據(jù)的write_cb,從而讀入數(shù)據(jù)。
8、 不斷重復上一個步驟,直到數(shù)據(jù)被讀完,此時libcurl會把socket描述符設置為刪除狀態(tài),所以multi_sock_cb會被回調,負責清理資源。而且,curl_multi_info_read會判斷已經(jīng)讀完數(shù)據(jù),可以在這里進行數(shù)據(jù)轉發(fā),最終進行資源清理。注意,最終讀到的數(shù)據(jù),會在write_cb設置的data中(前提是你有在write_cb中保存下來哈哈哈~)。
總結:
這庫使用起來十分奇怪,我看了幾天才看懂用法,我這篇博文寫得十分簡陋,最好的學習方法還是把demo跑一遍,看看打印出來的日志,還有詳細的參數(shù)設置,需要去看官網(wǎng)文檔。
總結
以上是生活随笔為你收集整理的[转] libcurl异步方式使用总结(附流程图)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 软碟通制作win10镜像,无法打开ins
- 下一篇: 记录一款Unity VR视频播放器插件的