libcurl多執行緒超時設定不安全

小 樓 一 夜 聽 春 雨發表於2014-12-01

from http://blog.csdn.net/sctq8888/article/details/10031219

(1), 超時(timeout)

libcurl 是 一個很不錯的庫,支援http,ftp等很多的協議。使用庫最大的心得就是,不仔細看文件,僅僅看著例子就寫程式,是一件危險的事情。我的程式崩潰了,我 懷疑是自己程式碼寫的問題,後來發現是庫沒用對。不仔細看文件(有時候文件本身也比較差勁,這時除了看仔細外,還要多動腦子,考慮它是怎麼實現的),後果很 嚴重。不加思索的使用別人的庫或者程式碼,有時候很愜意,但是出問題時,卻是寢食難安的。 

1. CURLcode curl_global_init(long flags); 在多執行緒應用中,需要在主執行緒中呼叫這個函式。這個函式設定libcurl所需的環境。通常情況,如果不顯式的呼叫它,第一次呼叫 curl_easy_init()時,curl_easy_init 會呼叫 curl_global_init,在單執行緒環境下,這不是問題。但是多執行緒下就不行了,因為curl_global_init不是執行緒安全的。在多個線 程中呼叫curl_easy_int,然後如果兩個執行緒同時發現curl_global_init還沒有被呼叫,同時呼叫 curl_global_init,悲劇就發生了。這種情況發生的概率很小,但可能性是存在的。

2. libcurl 有個很好的特性,它甚至可以控制域名解析的超時。但是在預設情況下,它是使用alarm + siglongjmp 實現的。用alarm在多執行緒下做超時,本身就幾乎不可能。如果只是使用alarm,並不會導致程式崩潰,但是,再加上siglongjmp,就要命了 (程式崩潰的很可怕,core中幾乎看不出有用資訊),因為其需要一個sigjmp_buf型的全域性變數,多執行緒修改它。(通常情況下,可以每個執行緒一個 sigjmp_buf 型的變數,這種情況下,多執行緒中使用 siglongjmp 是沒有問題的,但是libcurl只有一個全域性變數,所有的執行緒都會用)。

具體是類似 curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30L) 的超時設定,導致alarm的使用(估計發生在域名解析階段),如前所述,這在多執行緒中是不行的。解決方式是禁用掉alarm這種超時, curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L)。

這樣,多執行緒中使用超時就安全了。但是域名解析就沒了超時機制,碰到很慢的域名解析,也很麻煩。文件的建議是 Consider building libcurl with c-ares support to enable asynchronous DNS lookups, which enables nice timeouts for name resolves without signals. c-ares 是非同步的 DNS 解決方案。

引自:http://gcoder.blogbus.com/logs/54871550.html

呼叫libcurl下載,然後使用netstat檢視發現有大量的TCP連線保持在CLOSE_WAIT狀態
檢視libcurl的文件說明,有這樣一個選項:

CURLOPT_FORBID_REUSE

Pass a long. Set to 1 to make the next transfer explicitly close the connection when done. Normally, libcurl keeps all connections alive when done with one transfer in case a succeeding one follows that can re-use them. This option should be used with caution and only if you understand what it does. Set to 0 to have libcurl keep the connection open for possible later re-use (default behavior). 

也就是說,預設情況下libcurl完成一個任務以後,出於重用連線的考慮不會馬上關閉
如果沒有新的TCP請求來重用這個連線,那麼只能等到CLOSE_WAIT超時,這個時間預設在7200秒甚至更高,太多的CLOSE_WAIT連線會導致效能問題

解決方法:


curl_easy_setopt(curl, CURLOPT_FORBID_REUSE, 1); 

最好再修改一下TCP引數調低CLOSE_WAIT和TIME_WAIT的超時時間

相關文章