C++libcurl的使用

一、libcurl描述:

  在curl的官方網站 **//curl.haxx.se/download.html** 提供編譯好libcurl包,

   最後寫一個demod工程,演示下libcurl強大的威力和自己使用libcurl的一些心得。

二、 curl/libcurl 介紹

     先看的baidu 百科是怎麼介紹curl

它支持很多協議:FTP, FTPS, HTTP, HTTPS, GOPHER, TELNET, DICT, FILE 以及 LDAP。

curl同樣支持HTTPS認證,HTTP POST方法, HTTP PUT方法, FTP上傳, kerberos認證,HTTP上傳,代理服務器, cookies, 用戶名/密碼認證, 下載文件斷點續傳,上載文件斷點續傳,http代理服務器管道( proxy tunneling), 甚至它還支持IPv6, socks5代理服務器,通過http代理服務器上傳文件到FTP服務器等等,功能十分強大。

Windows操作系統下的網絡螞蟻網際快車(FlashGet)的功能它都可以做到。準確的說,curl支持文件的上傳和下載,所以是一個綜合傳輸工具,但是按照傳統,用戶習慣稱curl為下載工具。          

     

     libcurl就是curl的庫,其中在C++/C 在程序涉及到url協議通迅的時候,libcurl是首選。現在流行的網盤客戶端(百度的,360的),在與服務器回話的,都是使用libcurl作為底層庫。

這是個重點,根據我所了解的libcurl不能作為服務器,你可以用它去連接其他的服務器,或者用於極光推送一類的。

如果想寫一個http服務器的話可以用mongoose。

三、 安裝OpenSsl開發包

     libcurl主要通過SSL(Secure Sockets Layer 安全套接層)和TLS(Transport Layer Security 傳輸層安全),在傳輸層對網絡連接進行加密。

     本文使用OpenSSL使用作為開發庫。 也可以使用GnuTLS來支持TLS/SSL協議

     libcurl要支持ssh協議可以通過libssh2庫進行支持。

     下載OpenSsl的包  本文使用 ftp://ftp.openssl.org/snapshot/openssl-1.0.2-stable-SNAP-20140704.tar.gz

     用解壓工具解壓包解壓到硬盤上,方便可以解壓大C:\openssl-1.0.2-stable-SNAP-20140704

     解壓工具haozip (//haozip.2345.com/)

四、  安裝perl程序

    openssl編譯 Configure的時候,使用到perl程序,在window 下比較流行當仁不讓就是 activeperl

  //dl.softmgr.qq.com/original/Development/ActivePerl-5.16.3.1604-MSWin32-x64-298023.msi

    安裝比較簡單,默認安裝就可以了 ,來幾張截圖

   

 

五、  安裝NASM 彙編器

   編譯可以使用vc++編譯器來編譯OpenSSL,使用彙編器編譯有更快的速度,本人使用彙編器是nasm
    //www.nasm.us/pub/nasm/releasebuilds/2.11.05/win32/nasm-2.11.05-installer.exe 

   安裝過程如下圖所示

  

  

六、  使用彙編器NASM編譯OpenSSL庫

   本人安裝是VS2010開發環境, OpenSSL需要使用Visual Studio 命令行方式編譯。

      1) 點擊 開始->所有程序->Microsoft Visual Studio 2010->Visual Studio Tools->Visual Studio 命令提示(2010)

      2)再打開編譯窗口,將上面安裝NASM的路徑添加到PATH環境變量里  PATH=%PATH%;”c:\Program Files (x86)\nasm”
      3)進入OpenSSL的工作目錄 C:\openssl-1.0.2-stable-SNAP-20140704

      4)  新建一個編譯好的程序的輸出目錄 mkdir C:\openssl_lib

      

       5) 配置OpenSSL的安裝目錄 perl Configure VC-WIN32  –prefix=c:/openssl_lib

          輸出結果如下

         

        6) 生存Makefile 文件 ms\do_nasm

         如果不是使用nasm彙編器,這一步使用其他的命令代代替(如ms\do_ms)

         輸出如下

         

         7)開始編譯如果要編譯成動態庫使用  nmake -f ms\ntdll.mak

              如果要編譯成靜態庫使用nmake -f ms\nt.mak

              本次編譯成靜態鏈接庫,所以使用nmake -f ms\nt.mak

              需要等待一段時間。  可以運動下。

        8) 安裝到配置目錄

             所以使用nmake -f ms\nt.mak install 

             前面第5步 已經設置好了安裝目錄 c:/openssl_lib ,現在去裏面看看 是什麼

            

 七、 編譯curl/libcurl

      將下載curl-7.37.0.tar的包,解壓到一個文件目錄,本例解壓到E:\curl-7.37.0\

   進入winbuild目錄,通過 nmake /f Makefile.vc 查看下幫助

      

      執行 nmake /f Makefile.vc mode=dll VC=10 WITH_DEVEL=C:\openssl_lib WITH_SSL=static ENABLE_SSPI=no ENABLE_IPV6=no

  

     命令注釋 mode=dll 編譯libcurl位動態鏈接庫,如果static 就是把libcurl編譯位靜態庫 

     VC=10 代表使用的是VC2010

     WITH_DEVEL=C:\openssl_lib 表示用到第三方開發包的目錄,本例上面已經將openssl編譯好的開發包,安裝到此目錄

     WITH_SSL=static ,代表使用libssl庫 是靜態庫

     ENABLE_SSPI=no ENABLE_IPV6=no(禁用SSPI,ipV6功能)

     稍等片刻就編譯成功了。

八、  測試 編譯後的libcurl是否支持HTTPS協議 ?

       在第7步,編譯成功以後,會生成libcurl 還有外殼程序curl .

       安裝上面的配置,找到curl和libcurl

      

       

        下面測試 www.baidu.com 和支持加密 //mail.qq.com

        

         經過測試訪問加密HTTPS協議傳輸,和不用加密的HTTP都能成功。。  所以編譯libcurl 就算圓滿成功 大功告成!!!

ps:整個步驟其實是轉載的,不過樓主按照這個步驟最後確實成功了。自己懶得再來一遍截圖了。

 其實最主要的還是libcurl.lib這個庫,本身這個庫是支持http的 但是不支持https。所以需要用的openssl,先安裝了openssl,然後再編譯libcurl,會把其編譯進去,從而生成的新的libcurl可以支持https。極光推送用的就是https。

九、libcurl是一個跨平台的網絡協議庫,支持http, https, ftp, gopher, telnet, dict, file, 和ldap 協議。libcurl同樣支持HTTPS證書授權,HTTP POST, HTTP PUT, FTP 上傳, HTTP基本表單上傳,代理,cookies,和用戶認證。

在基於LibCurl的程序里,主要採用callback function (回調函數)的形式完成傳輸任務,用戶在啟動傳輸前設置好各類參數和回調函數,當滿足條件時libcurl將調用用戶的回調函數實現特定功能。
  libcurl主要提供了兩種發送http請求的方式,分別是Easy interface方式和multi interface方式,前者是採用阻塞的方式發送單條數據,後者採用組合的方式可以一次性發送多條數據

壹、Easy interface

libcurl的easy interface是最基本的用法,簡要流程為:
1、在主線程中調用curl_global_init(CURL_GLOBAL_ALL)初始化
2、調用curl_easy_init獲取一個句柄;
3、調用curl_easy_setopt函數設置此次傳輸的一些基本參數,如url地址、http頭、cookie信息、發送超時時間等,其中,CURLOPT_URL是必設的選項;
4、設置完成後,調用curl_easy_perform函數發送數據;
5、數據發送完畢後,調用curl_easy_cleanup清空句柄;
6、調用curl_global_cleanup()做清理工作。

在整過過程中設置curl_easy_setopt()參數是最關鍵的,幾乎所有的libcurl程序都要使用它。

貳、一些基本的函數

 
      1.CURLcode curl_global_init(long flags);
描述:
這個函數只能用一次。(其實在調用curl_global_cleanup 函數後仍然可再用)
如果這個函數在curl_easy_init函數調用時還沒調用,它講由libcurl庫自動調用,所以多線程下最好主動調用該函數以防止在線程中curl_easy_init時多次調用。
注意:雖然libcurl是線程安全的,但curl_global_init是不能保證線程安全的,所以不要在每個線程中都調用curl_global_init,應該將該函數的調用放在主線程中。
參數:flags
CURL_GLOBAL_ALL                      //初始化所有的可能的調用。
CURL_GLOBAL_SSL                      //初始化支持 安全套接字層。
CURL_GLOBAL_WIN32            //初始化win32套接字庫。
CURL_GLOBAL_NOTHING         //沒有額外的初始化
 
2 void curl_global_cleanup(void);
描述:在結束libcurl使用的時候,用來對curl_global_init做的工作清理。類似於close的函數。
注意:雖然libcurl是線程安全的,但curl_global_cleanup是不能保證線程安全的,所以不要在每個線程中都調用curl_global_init,應該將該函數的調用放在主線程中。
3 char *curl_version( );
描述: 打印當前libcurl庫的版本。
4 CURL *curl_easy_init( );
描述:
curl_easy_init用來初始化一個CURL的指針(有些像返回FILE類型的指針一樣). 相應的在調用結束時要用curl_easy_cleanup函數清理.
一般curl_easy_init意味着一個會話的開始. 它會返回一個easy_handle(CURL*對象), 一般都用在easy系列的函數中.
5 void curl_easy_cleanup(CURL *handle);
描述:
   這個調用用來結束一個會話.與curl_easy_init配合著用. 
參數:
CURL類型的指針.
6 CURLcode curl_easy_setopt(CURL *handle, CURLoption option, parameter);
描述: 這個函數最重要了.幾乎所有的curl 程序都要頻繁的使用它.它告訴curl庫.程序將有如何的行為. 比如要查看一個網頁的html代碼等.(這個函數有些像ioctl函數)參數:
1 CURL類型的指針
2 各種CURLoption類型的選項.(都在curl.h庫里有定義,man 也可以查看到)
3 parameter 這個參數 既可以是個函數的指針,也可以是某個對象的指針,也可以是個long型的變量.它用什麼這取決於第二個參數.
CURLoption 這個參數的取值很多.具體的可以查看man手冊.
7 CURLcode curl_easy_perform(CURL *handle);
描述:這個函數在初始化CURL類型的指針 以及curl_easy_setopt完成後調用. 就像字面的意思所說perform就像是個舞台.讓我們設置的option 運作起來.
參數:
CURL類型的指針.

叄、 curl_easy_setopt函數部分選項介紹

該函數是curl中非常重要的函數,curl所有設置都是在該函數中完成的,該函數的設置選項眾多,注意本節的闡述的只是部分常見選項。
1.     CURLOPT_URL 
設置訪問URL
2.       CURLOPT_WRITEFUNCTION,CURLOPT_WRITEDATA
複製代碼
static size_t OnWriteFile(void* buffer, size_t size, size_t nmemb, void* lpVoid)
{
    FILE* stream = (FILE *)lpVoid;
    if (nullptr == stream || nullptr == buffer)
    {
        return -1;
    }
    size_t nWrite = fwrite(buffer, size, nmemb, stream);
    return nWrite;
}

curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, OnWriteFile);     //寫入數據的回調
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)m_file);  
複製代碼

回調函數原型為:size_t function( void *ptr, size_t size, size_t nmemb, void *stream); 函數將在libcurl接收到數據後被調用,因此函數多做數據保存的功能,如處理下載文件。CURLOPT_WRITEDATA 用於表明CURLOPT_WRITEFUNCTION函數中的stream指針的來源。

如果你沒有通過CURLOPT_WRITEFUNCTION屬性給easy handle設置回調函數,libcurl會提供一個默認的回調函數,它只是簡單的將接收到的數據打印到標準輸出。你也可以通過 CURLOPT_WRITEDATA屬性給默認回調函數傳遞一個已經打開的文件指針,用於將數據輸出到文件里。
3.      CURLOPT_HEADERFUNCTION,CURLOPT_HEADERDATA
複製代碼
static size_t OnHeader(void* buffer, size_t size, size_t nmemb, void* lpVoid)
{
    std::string* str = dynamic_cast<std::string*>((std::string *)lpVoid);
    if (nullptr == str || nullptr == buffer)
    {
        return -1;
    }
    
    char* pData = (char*)buffer;
    str->append(pData, size * nmemb);      //str後面追加pData
    return nmemb;
}

curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, OnHeader);                   //寫入接受頭的回調
std::string header;
curl_easy_setopt(curl, CURLOPT_HEADERDATA, &header);  
複製代碼

回調函數原型為 size_t function( void *ptr, size_t size,size_t nmemb, void *stream); libcurl一旦接收到http 頭部數據後將調用該函數。CURLOPT_WRITEDATA 傳遞指針給libcurl,該指針表明CURLOPT_HEADERFUNCTION 函數的stream指針的來源。

4.       CURLOPT_READFUNCTION CURLOPT_READDATA  curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, OnWriteFile); //寫入數據的回調  curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)m_file); //數據指針傳遞給寫回調
複製代碼
 
static size_t OnWriteFile(void* buffer, size_t size, size_t nmemb, void* lpVoid)
{
FILE* stream = (FILE *)lpVoid;
if (nullptr == stream || nullptr == buffer)
{
return -1;
}
size_t nWrite = fwrite(buffer, size, nmemb, stream);
return nWrite;
}
複製代碼

libCurl需要讀取數據傳遞給遠程主機時將調用CURLOPT_READFUNCTION指定的函數,函數原型是:size_t function(void *ptr, size_t size, size_t nmemb,void *stream). CURLOPT_READDATA 表明CURLOPT_READFUNCTION函數原型中的stream指針來源。

5.       CURLOPT_NOPROGRESS,CURLOPT_PROGRESSFUNCTION,CURLOPT_PROGRESSDATA
複製代碼
 static size_t OnProgress(void *clientp, double dltotal, double dlnow, double ultotal, double ulnow) {
    curl_http_downloader* dd = (curl_http_downloader*)clientp;

    if (dd->m_progress) {
        dd->m_progress(dltotal,dlnow,ultotal,ulnow);
    }
    return 0;
};
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0); //關閉進度表
curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, OnProgress); //過時的回調進度表
curl_easy_setopt(curl, CURLOPT_RESUME_FROM, LocalFilelen);
複製代碼

跟數據傳輸進度相關的參數。CURLOPT_PROGRESSFUNCTION 指定的函數正常情況下每秒被libcurl調用一次,為了使CURLOPT_PROGRESSFUNCTION被調用,CURLOPT_NOPROGRESS必須被設置為false,CURLOPT_PROGRESSDATA指定的參數將作為CURLOPT_PROGRESSFUNCTION指定函數的第一個參數

6.       CURLOPT_TIMEOUT,CURLOPT_CONNECTIONTIMEOUT:
CURLOPT_TIMEOUT 由於設置傳輸時間,CURLOPT_CONNECTIONTIMEOUT 設置連接等待時間
7.       CURLOPT_FOLLOWLOCATION
設置重定位URL
8.       CURLOPT_RANGE: CURLOPT_RESUME_FROM:
斷點續傳相關設置。CURLOPT_RANGE 指定char *參數傳遞給libcurl,用於指明http域的RANGE頭域,例如:
表示頭500個位元組:bytes=0-499
表示第二個500位元組:bytes=500-999
表示最後500個位元組:bytes=-500
表示500位元組以後的範圍:bytes=500-
第一個和最後一個位元組:bytes=0-0,-1
同時指定幾個範圍:bytes=500-600,601-999
CURLOPT_RESUME_FROM 傳遞一個long參數給libcurl,指定你希望開始傳遞的 偏移量。

肆、 curl_easy_perform 函數說明(error 狀態碼)

該函數是完成curl_easy_setopt指定的所有選項,本節重點介紹curl_easy_perform的返回值。返回0意味一切ok,非0代表錯誤發生。主要錯誤碼說明:
1.    CURLE_OK 
    任務完成一切都好
2     CURLE_UNSUPPORTED_PROTOCOL
    不支持的協議,由URL的頭部指定
3     CURLE_COULDNT_CONNECT
    不能連接到remote 主機或者代理
4     CURLE_REMOTE_ACCESS_DENIED
    訪問被拒絕
5     CURLE_HTTP_RETURNED_ERROR
    Http返回錯誤
6     CURLE_READ_ERROR
讀本地文件錯誤

要獲取詳細的錯誤描述字符串,可以通過const char *curl_easy_strerror(CURLcode errornum ) 這個函數取得.
 
 
                                                                                                                                   改變自己,從現在做起———–久館