Libcurl為一個免費開源的,用戶端url傳輸庫,本文主要分析使用過程中遇到的卡死問題。
問題描述
LibCurl使用阻塞的方式來進行http下載,curl_easy_perform執行後,程式會在這裡阻塞等待下載結束(成功OR失敗),此時若下載一段時間後出現網路異常,curl_easy_perform不會返回失敗,整個線程阻塞卡死。
問題分析
複現:串連無線網路,然後執行curl_easy_perform下載,下載過程中,斷開無線網路,curl_easy_perform卡死,阻塞整個線程。
原因:當斷開無線網路時,使用命令列netstat ano | findstr ‘串連ip’, 可以發現LibCurl的http串連並沒有斷開(不知道是不是windows系統的bug),如果將CURLOPT_TIMEOUT設定為無限等待,curl_easy_perform便會一直阻塞住線程。
解決方案
1、如果我們不將CURLOPT_TIMEOUT設定為無限等待,是不會出現上面問題,curl_easy_perform會在執行一段時間(由CURLOPT_TIMEOUR設定),結束並返回失敗,但是如果設定合適的CURLOPT_TIMEOUT是個問題。
1
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 0); // blocking forever, cannot find a suitable value
2、使用multi模式下載, 而不使用easy模式, 此方法的唯一好處就是multi並不會阻塞, 而是立即返回. 但是缺點是帶來了問題, 其一就是需要自己去阻塞, 當我們需要返回時再返回, 其二還需要啟動一個線程, 需要自己控制整個過程的節奏。
3、在下載中, 另起一個線程, 若發現下載狀態卡死(可以通過定期檢查檔案大小來實現), 則從外部中斷下載線程. 此方法需另起線程, 而且直接中斷線程, 會給整個程式帶來不穩定。
4、設定CURLOPT_TIMEOUT為30s,以30s為間隔斷點續傳。
5、使用CURLOPT_LOW_SPEED_LIMIT, CURLOPT_LOW_SPEED_TIME
res = curl_easy_setopt(curl, CURLOPT_LOW_SPEED_LIMIT, 1024L);
res = curl_easy_setopt(curl, CURLOPT_LOW_SPEED_TIME, 30L);
6、使用CURLOPT_PROGRESSFUNCTION設定進度表回呼函數,可以在該回呼函數內部檢測下載是否出錯,如果出現就中斷curl_easy_perform,返回下載錯誤。
#include <curl/curl.h>
int progress_callback(void *clientp, double dltotal, double dlnow, double ultotal, double ulnow);
CURLcode curl_easy_setopt(CURL *handle, CURLOPT_PROGRESSFUNCTION, progress_callback);