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