{"id":197,"date":"2019-04-12T23:57:41","date_gmt":"2019-04-12T15:57:41","guid":{"rendered":"http:\/\/www.sinkland.cn\/?p=197"},"modified":"2019-08-23T21:37:53","modified_gmt":"2019-08-23T13:37:53","slug":"libcurl%e5%ae%9e%e7%8e%b0%e5%a4%9a%e7%ba%bf%e7%a8%8b%e4%b8%8b%e8%bd%bd","status":"publish","type":"post","link":"http:\/\/www.sinkland.cn\/?p=197","title":{"rendered":"LibCurl\u5b9e\u73b0\u591a\u7ebf\u7a0b\u4e0b\u8f7d"},"content":{"rendered":"<blockquote><p>\n  libcurl\u7248\u672c: curl-7.61.1\n<\/p><\/blockquote>\n<h4>\u7279\u6027:<\/h4>\n<ul>\n<li>\u591a\u7ebf\u7a0b\u4e0b\u8f7d\uff0c\u5e73\u5747\u901f\u5ea6\u6bd4\u5355\u7ebf\u7a0b\u5feb\u5f88\u591a<\/li>\n<li>\u5931\u8d25\u591a\u6b21\u91cd\u8bd5\uff0c\u80fd\u9002\u5e94\u7f51\u7edc\u8f83\u5dee\u73af\u5883<\/li>\n<li>\u652f\u6301http\/https<\/li>\n<\/ul>\n<pre><code class=\"language-c \">\/\/ CurlDownload.cpp\n\n#include \"stdafx.h\"\n#include \"CurlDownload.h\"\n\nCRITICAL_SECTION CurlDownload::m_csFileLock = { 0 };\n\nCurlDownload::CurlDownload(void)\n{\n    m_sharedns_handle = NULL;\n    Init();\n}\n\nCurlDownload::~CurlDownload(void)\n{\n\n}\n\nCurlDownload* CurlDownload::GetInstense()\n{\n    static CurlDownload* pCurlDownld = NULL;\n    if (pCurlDownld == NULL)\n    {\n        pCurlDownld = new CurlDownload();\n    }\n    return pCurlDownld;\n}\n\nBOOL CurlDownload::Init()\n{\n    CURLcode code = curl_global_init(CURL_GLOBAL_ALL);\n    if (m_sharedns_handle == NULL)  \n    {  \n        m_sharedns_handle = curl_share_init();  \n        curl_share_setopt(m_sharedns_handle, CURLSHOPT_SHARE, CURL_LOCK_DATA_DNS);  \n    }\n    return TRUE;\n}\n\nCURL* CurlDownload::CreateShareCurl()\n{  \n    CURL *curl = curl_easy_init();\n    if (curl == NULL)\n    {\n        return NULL;\n    }\n\n    curl_easy_setopt(curl, CURLOPT_SHARE, m_sharedns_handle); \n    curl_easy_setopt(curl, CURLOPT_DNS_CACHE_TIMEOUT, 60 * 5);\n    return curl;\n}\n\nCURL* CurlDownload::CreateNodeCurl(PCHAR szUrl, DownNode* pDownNode)\n{\n    CURL* curl = CreateShareCurl();\n    if (curl == NULL)\n    {\n        return NULL;\n    }\n\n    CURLcode code;\n\n    code = curl_easy_setopt(curl, CURLOPT_URL, szUrl);\n    if (code != CURLE_OK)\n    {\n        goto END;\n    }\n\n    CheckSSL(curl, string(szUrl));\n\n    code = curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &amp;CurlDownload::WriteData);  \n    if (code != CURLE_OK) \n    {\n        goto END;\n    }\n\n    code = curl_easy_setopt(curl, CURLOPT_WRITEDATA, pDownNode); \n    if (code != CURLE_OK) \n    {\n        goto END;\n    }\n\n    code = curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); \n    if (code != CURLE_OK) \n    {\n        goto END;\n    }\n    code = curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L);  \n    if (code != CURLE_OK) \n    {\n        goto END;\n    }\n\n    \/\/ \u901f\u5ea6\u8fc7\u6162\uff0c\u7ec8\u6b62\u8fde\u63a5\n    code = curl_easy_setopt(curl, CURLOPT_LOW_SPEED_LIMIT, 1L);  \n    if (code != CURLE_OK) \n    {\n        goto END;\n    }\n    code = curl_easy_setopt(curl, CURLOPT_LOW_SPEED_TIME, 30L); \n    if (code != CURLE_OK) \n    {\n        goto END;\n    }\n\n    \/\/\u9650\u5236\u8fde\u63a5\u8d85\u65f6\n    code = curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, MM_TIMEOUT);\n    if (code != CURLE_OK) \n    {\n        goto END;\n    }\n\n    return curl;\n\nEND:\n    if (curl != NULL)\n    {\n        curl_easy_cleanup(curl);\n    }\n    return NULL;\n}\n\nBOOL CurlDownload::CheckSSL(CURL* pCurl, const std::string&amp; strUrl)\n{\n    if (pCurl == NULL || strUrl.empty())\n    {\n        return FALSE;\n    }\n\n    string strHead = strUrl.substr(0, 5);\n    transform(strHead.begin(), strHead.end(), strHead.begin(), tolower);\n    if (strHead != \"https\")\n    {\n        return FALSE;\n    }\n\n    curl_easy_setopt(pCurl, CURLOPT_USE_SSL, CURLUSESSL_TRY);\n    curl_easy_setopt(pCurl, CURLOPT_SSL_VERIFYPEER, 0);\n    curl_easy_setopt(pCurl, CURLOPT_SSL_VERIFYHOST, 1);\n    return TRUE;\n}\n\nCURL_RET CurlDownload::GetFileLength(const string&amp; strFileUrl, string&amp; strRedirectUrl, INT64* pnDownloadFileLenth)\n{\n    CURL_RET ret = CURL_RET_FAILD;\n\n    if (pnDownloadFileLenth == NULL)\n    {\n        return CURL_RET_FAILD;\n    }\n\n    CURL *curl = CreateShareCurl(); \n    if (curl == NULL)\n    {\n        return CURL_RET_FAILD;\n    }\n\n    curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, \"GET\");       \n    curl_easy_setopt(curl, CURLOPT_URL, strFileUrl.c_str());\n    CheckSSL(curl, strFileUrl);\n    curl_easy_setopt(curl, CURLOPT_HEADER, 1); \n    curl_easy_setopt(curl, CURLOPT_NOBODY, 1);\n    curl_easy_setopt(curl, CURLOPT_TIMEOUT, MM_TIMEOUT);\n    curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L);\n\n    LONG lHttpCode = 0;\n    DOUBLE dbDownloadFileLenth  = -1;\n\n    CURLcode code = curl_easy_perform(curl);\n    if (code == CURLE_OK)  \n    {\n        curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &amp;lHttpCode);\n\n        \/\/\u9047\u5230\u91cd\u5b9a\u5411\uff0c\u83b7\u53d6\u91cd\u5b9a\u5411\u540e\u7684url\n        if (lHttpCode == 301 || lHttpCode == 302)\n        {\n            char *redirecturl = NULL;\n            curl_easy_getinfo(curl, CURLINFO_REDIRECT_URL, &amp;redirecturl);\n\n            if (redirecturl != NULL)\n            {\n                strRedirectUrl = redirecturl;\n                ret = CURL_RET_REDIRECT;\n            }\n            else\n            {\n                ret = CURL_RET_NEED_RETRY;\n            }\n        }\n        else if (lHttpCode &gt;= 200 &amp;&amp; lHttpCode &lt; 300)\n        {\n            curl_easy_getinfo(curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &amp;dbDownloadFileLenth); \n            ret = CURL_RET_SUCCESS;\n        }\n        \/\/\u8d44\u6e90\u4e0d\u5b58\u5728\uff0c\u76f4\u63a5\u8fd4\u56de\n        else if (lHttpCode == 404)\n        {\n            ret = CURL_RET_FAILD;\n        }\n    }\n    else\n    {\n        ret = CURL_RET_NEED_RETRY;\n    }\n\n    if (ret == CURL_RET_SUCCESS)\n    {\n        INT64 nDownloadFileLenth = (INT64)dbDownloadFileLenth;   \n        if (nDownloadFileLenth == -1 || nDownloadFileLenth == 0)\n        {\n            ret = CURL_RET_NEED_RETRY;\n        }\n        else\n        {\n            *pnDownloadFileLenth = nDownloadFileLenth;\n        }\n    }\n\n    curl_easy_cleanup(curl);\n    return ret;\n}\n\nCURL_RET CurlDownload::TryGetFileLength(const string&amp; strFileUrl, string&amp; strRedirectUrl, INT64* pnDownloadFileLenth)\n{\n    CURL_RET ret = CURL_RET_FAILD;\n\n    strRedirectUrl.clear();\n    string strFileUrlTemp = strFileUrl;\n    string strRedirectUrlTemp;\n\n    for (UINT i = 0; i &lt; MAX_RETRY_TIMES; i++)\n    {\n        ret = GetFileLength(strFileUrlTemp, strRedirectUrlTemp, pnDownloadFileLenth);\n        if (ret == CURL_RET_SUCCESS ||\n            ret == CURL_RET_FAILD)\n        {\n            break;\n        }\n        else if (ret == CURL_RET_REDIRECT)\n        {\n            strFileUrlTemp = strRedirectUrlTemp;\n        }\n\n        if (ret == CURL_RET_NEED_RETRY &amp;&amp;\n            i != MAX_RETRY_TIMES - 1)\n        {\n            float fDelaySec = pow(2.0, (INT)i);\n            Sleep(fDelaySec * 1000);\n        }\n    }\n\n    if (ret == CURL_RET_SUCCESS)\n    {\n        \/\/\u88ab\u91cd\u5b9a\u5411\u4e86\n        if (strFileUrl != strFileUrlTemp)\n        {\n            strRedirectUrl = strFileUrlTemp;\n        }\n    }\n    return ret;\n}\n\nsize_t CurlDownload::HeaderInfo(char *ptr, size_t size, size_t nmemb, void *userdata)\n{\n    std::string* pHead = (std::string*)userdata;\n    if (pHead != NULL)\n    {\n        pHead-&gt;append(std::string((char*)ptr, nmemb));\n    }\n    return size * nmemb;\n}\n\nCURL_RET CurlDownload::CheckIsSupportRange(const string&amp; strUrl, BOOL *pbSupportMultiDown)\n{\n    if (pbSupportMultiDown == NULL)\n    {\n        return CURL_RET_FAILD;\n    }\n\n    CURL* curl = CreateShareCurl();\n    if (curl == NULL)\n    {\n        return CURL_RET_FAILD;\n    }\n\n    curl_easy_setopt(curl, CURLOPT_URL, strUrl.c_str());  \n    CheckSSL(curl, strUrl);\n    curl_easy_setopt(curl, CURLOPT_HEADER, 1); \n    curl_easy_setopt(curl, CURLOPT_NOBODY, 1);\n\n    string strHeader;\n    curl_easy_setopt(curl, CURLOPT_HEADERDATA, &amp;strHeader);\n    curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, &amp;CurlDownload::HeaderInfo);\n    curl_easy_setopt(curl, CURLOPT_RANGE, \"0-\");\n    curl_easy_setopt(curl, CURLOPT_TIMEOUT, MM_TIMEOUT);\n    curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L);\n    CURLcode code = curl_easy_perform(curl);\n    curl_easy_cleanup(curl);\n\n    if (code == CURLE_OK)\n    {\n        if (strHeader.find(\"Content-Range: bytes\") != std::string::npos)\n        {\n            *pbSupportMultiDown = TRUE;\n        }\n        else\n        {\n            pbSupportMultiDown = FALSE;\n        }\n        return CURL_RET_SUCCESS;\n    }\n\n    return CURL_RET_NEED_RETRY;\n}\n\n\nCURL_RET CurlDownload::TryCheckIsSupportRange(const string&amp; strUrl, BOOL *pbSupportMultiDown)\n{\n    CURL_RET ret = CURL_RET_FAILD;\n\n    for (UINT i = 0; i &lt; MAX_RETRY_TIMES; i++)\n    {\n        ret = CheckIsSupportRange(strUrl, pbSupportMultiDown);\n        if (ret == CURL_RET_SUCCESS ||\n            ret == CURL_RET_FAILD)\n        {\n            break;\n        }\n\n        if (ret == CURL_RET_NEED_RETRY &amp;&amp;\n            i != MAX_RETRY_TIMES - 1)\n        {\n            float fDelaySec = pow(2.0, (INT)i);\n            Sleep(fDelaySec * 1000);\n        }\n    }\n    return ret;\n}\n\nsize_t CurlDownload::WriteData(char *ptr, size_t size, size_t nmemb, void *userdata)\n{\n    DownNode *pNode = (DownNode *)userdata;\n    if (pNode == NULL)\n    {\n        return 0;\n    }\n\n    \/\/\u5df2\u7ecf\u63a5\u6536\u5b8c\u6210\n    if (pNode-&gt;nRecvSize &gt;= pNode-&gt;nBlockSize)\n    {\n        return size * nmemb;\n    }\n    size_t uWriteLen = 0;\n\n    EnterCriticalSection(&amp;m_csFileLock);\n\n    if (_fseeki64(pNode-&gt;pFile, pNode-&gt;nStartPos + pNode-&gt;nRecvSize, SEEK_SET) != 0)\n    {\n        goto END;\n    }\n\n    INT64 nUnRecvSize = pNode-&gt;nBlockSize - pNode-&gt;nRecvSize;\n\n    size_t uToWriteCount = 0;\n    if (nUnRecvSize &gt; size * nmemb)\n    {\n        uToWriteCount = nmemb;\n    }\n    else\n    {\n        uToWriteCount = nUnRecvSize \/ size;\n    }\n    uWriteLen = fwrite (ptr, size, uToWriteCount, pNode-&gt;pFile);\n    pNode-&gt;nRecvSize += uWriteLen * size;\n\nEND:\n    LeaveCriticalSection(&amp;m_csFileLock);\n    return size * nmemb; \n}\n\n\nDWORD WINAPI CurlDownload::DownWorkThread(PVOID pParam)\n{\n    DownNode* pNode = (DownNode*)pParam;\n    if (pNode == NULL || pNode-&gt;curl == NULL)\n    {\n        return FALSE;\n    }\n\n    CURLcode code;\n    BOOL bRet           = FALSE;\n    BOOL bNeedRetry     = FALSE;\n    CURL* curl          = NULL;\n    DWORD dwRetryTimes  = 0;\n    INT64 nStartPos     = 0;\n    INT64 nUnRecvSize   = 0;\n    INT64 nRangeSize    = 0;\n    CHAR szRange[32]    = { 0 };\n\n    while(pNode-&gt;nRecvSize &lt; pNode-&gt;nBlockSize)\n    {\n        if (dwRetryTimes &gt; MAX_RETRY_TIMES)\n        {\n            goto END;\n        }\n        else if (bNeedRetry)\n        {\n            float fDelaySec = pow(2.0, (INT)dwRetryTimes);\n            Sleep(fDelaySec * 1000);\n\n            dwRetryTimes++;\n            bNeedRetry = FALSE;\n        }\n\n        if (pNode-&gt;bSupportMultiDown)\n        {\n            nStartPos = pNode-&gt;nStartPos + pNode-&gt;nRecvSize;\n            nUnRecvSize = pNode-&gt;nBlockSize - pNode-&gt;nRecvSize;\n\n            nRangeSize = pNode-&gt;nBlockSize - pNode-&gt;nRecvSize;\n            if (nRangeSize &gt; MMBLOCK_RANGE_SIZE)\n            {\n                nRangeSize = MMBLOCK_RANGE_SIZE;\n            }\n\n            StringCchPrintfA(szRange, sizeof(szRange), \"%lld-%lld\", nStartPos, nStartPos + nRangeSize);\n            code = curl_easy_setopt(pNode-&gt;curl, CURLOPT_RANGE, szRange);\n        }\n        else\n        {\n            \/\/\u5355\u7ebf\u7a0b\u603b\u662f\u4ece0\u5f00\u59cb\u4e0b\u8f7d\n            pNode-&gt;nRecvSize = 0;\n            code = CURLE_OK;\n        } \n        if (code != CURLE_OK)\n        {\n            bNeedRetry = TRUE;\n            continue;\n        }\n\n        code = curl_easy_perform(pNode-&gt;curl);\n        if (code != CURLE_OK)\n        {  \n            if (pNode-&gt;nRecvSize &gt; nRangeSize)\n            {\n                pNode-&gt;nRecvSize -= nRangeSize;\n            }\n            bNeedRetry = TRUE;\n            continue;\n        }\n    }\n\nEND:\n\n    if (pNode-&gt;curl != NULL)\n    {\n        curl_easy_cleanup(pNode-&gt;curl);\n    }\n    if (pNode != NULL)\n    {\n        free(pNode);\n    }\n    return bRet;\n}\n\nBOOL CurlDownload::DownLoad(const std::string &amp;strUrl, const std::string&amp; strDownloadPath)\n{\n    CURL_RET ret = CURL_RET_FAILD;\n    InitializeCriticalSection(&amp;m_csFileLock);\n\n    string strRedirectUrl;\n    INT64 nDownloadFileLenth = 0;\n\n    ret = TryGetFileLength(strUrl, strRedirectUrl, &amp;nDownloadFileLenth);\n    if (ret != CURL_RET_SUCCESS)\n    {\n        return FALSE;\n    }\n    DeleteFileA(strDownloadPath.c_str());\n\n    FILE *pFile = fopen (strDownloadPath.c_str (), \"wb\");\n    if (pFile == NULL)\n    {\n        return FALSE;\n    }\n\n    string strUrlFinal;\n    if (strRedirectUrl.empty())\n    {\n        strUrlFinal = strUrl;\n    }\n    else\n    {\n        strUrlFinal = strRedirectUrl;\n    }\n\n    \/\/\u786e\u5b9a\u4e0b\u8f7d\u7ebf\u7a0b\u4e2a\u6570\n    DWORD dwThreadCount = (DWORD)ceil(1.0 * nDownloadFileLenth \/ (MMBLOCK_SIZE));\n    if (dwThreadCount &gt; MAX_DOWN_THREAD_COUNT)\n    {\n        dwThreadCount = MAX_DOWN_THREAD_COUNT;\n    }\n\n    BOOL bSupportMultiDown = FALSE;\n    if (dwThreadCount &gt; 1)\n    {\n        TryCheckIsSupportRange(strUrlFinal, &amp;bSupportMultiDown);\n        if (!bSupportMultiDown)\n        {\n            dwThreadCount = 1;\n        }\n    }\n\n    DWORD dwRet = WAIT_FAILED;\n\n    \/\/\u6587\u4ef6\u5206\u5757\u5927\u5c0f\n    INT64 nBlockSize = nDownloadFileLenth \/ dwThreadCount;\n    HANDLE hDownloadThread[MAX_DOWN_THREAD_COUNT] = { 0 };\n\n    for (UINT i = 0; i &lt; dwThreadCount; i++)\n    {\n        DownNode *pNode = new DownNode();\n        if (pNode == NULL)\n        {\n            return FALSE;\n        }\n        memset(pNode, 0, sizeof(DownNode));\n\n        pNode-&gt;nStartPos = i * nBlockSize;\n        if (i != dwThreadCount - 1)\n        {\n            pNode-&gt;nBlockSize = nBlockSize;\n        }\n        else\n        {\n            pNode-&gt;nBlockSize = nDownloadFileLenth - i * nBlockSize;\n        }\n\n        pNode-&gt;curl = CreateNodeCurl((PCHAR)strUrlFinal.c_str(), pNode);\n        if (pNode-&gt;curl == NULL)\n        {\n            goto END;\n        }\n\n        pNode-&gt;pFile                = pFile;\n        pNode-&gt;bSupportMultiDown    = bSupportMultiDown;\n        pNode-&gt;dwThreadIndex        = i;\n\n        hDownloadThread[i] = CreateThread(NULL, 0, DownWorkThread, pNode, 0, NULL); \n        if (hDownloadThread[i] == NULL)\n        {\n            goto END;\n        }\n    }\n\n    dwRet = WaitForMultipleObjects(dwThreadCount, hDownloadThread, TRUE, 120 * 60 * 1000);\nEND:\n\n    BOOL bDownLoadRet = FALSE;\n\n    if (pFile != NULL)\n    {\n        _fseeki64(pFile, 0, SEEK_END);\n        INT64 nFileLenth = _ftelli64(pFile);\n        if (nFileLenth == nDownloadFileLenth)\n        {\n            bDownLoadRet = TRUE;\n        }\n        fclose(pFile);\n    }\n\n    return bDownLoadRet ;\n}\n\nBOOL CurlDownload::DownLoad(const std::wstring &amp;strUrl, const std::wstring&amp;strDownloadPath)\n{\n    string strUrlA(strUrl.length(), ' ');\n    string strDownloadPathA(strDownloadPath.length(), ' ');\n\n    copy(strUrl.begin(), strUrl.end(), strUrlA.begin());\n    copy(strDownloadPath.begin(), strDownloadPath.end(), strDownloadPathA.begin());\n    return DownLoad(strUrlA, strDownloadPathA);\n}\n\n<\/code><\/pre>\n<p>\u5934\u6587\u4ef6:<\/p>\n<pre><code class=\"language-c \">\/\/ CurlDownload.h\n\n#pragma once\n#include \"include\/curl\/curl.h\"\n\n\/\/\u6700\u5927\u7ebf\u7a0b\u6570\n#define MAX_DOWN_THREAD_COUNT   10\n\/\/\u8d85\u65f6\u65f6\u95f4\n#define MM_TIMEOUT              60L\n\/\/\u5931\u8d25\u91cd\u8bd5\u6b21\u6570\n#define MAX_RETRY_TIMES         8\n\/\/\u6bcf\u4e2a\u7ebf\u7a0b\u8d1f\u8d23\u4f20\u8f93\u7684\u6570\u636e\u5757\u7684\u5927\u5c0f:10M\n#define MMBLOCK_SIZE            1024 * 1024 * 10\n\/\/\u7ebf\u7a0b\u5185\u90e8\u4e0b\u8f7d\u6bcf\u6b21\u5c1d\u8bd5\u4e0b\u8f7d\u7684\u5757\u5927\u5c0f:2M\n#define MMBLOCK_RANGE_SIZE      1024 * 1024 * 2\n\nenum CURL_RET\n{\n    CURL_RET_SUCCESS = 0,\n    CURL_RET_FAILD,\n    CURL_RET_NEED_RETRY,\n    CURL_RET_REDIRECT,\n};\n\nstruct DownNode\n{\n    CURL            *curl;\n    FILE            *pFile;\n\n    DWORD           dwThreadIndex;\n    BOOL            bSupportMultiDown;\n\n    INT64           nStartPos;\n    INT64           nBlockSize;\n    INT64           nRecvSize;\n};\n\nclass CurlDownload\n{\npublic:\n    CurlDownload(void);\n    ~CurlDownload(void);\n    static CurlDownload* GetInstense();\n\n    BOOL DownLoad(const std::string &amp;strUrl, const std::string&amp;strDownloadPath);\n    BOOL DownLoad(const std::wstring &amp;strUrl, const std::wstring&amp;strDownloadPath);\n\nprivate:\n    BOOL Init();\n    CURL* CreateShareCurl();\n    CURL* CreateNodeCurl(PCHAR szUrl, DownNode* pNode);\n    static DWORD WINAPI CurlDownload::DownWorkThread(PVOID pParam);\n    BOOL CheckSSL(CURL* pCurl, const std::string&amp; strUrl);\n    CURL_RET GetFileLength(const string&amp; strFileUrl, string&amp; strRedirectUrl, INT64* pnDownloadFileLenth);\n    CURL_RET TryGetFileLength(const string&amp; strFileUrl, string&amp; strRedirectUrl, INT64* pnDownloadFileLenth);\n\n    static size_t WriteData(char *ptr, size_t size, size_t nmemb, void *userdata);\n    static size_t HeaderInfo(char *ptr, size_t size, size_t nmemb, void *userdata);    \n    CURL_RET CheckIsSupportRange(const string&amp; strUrl, BOOL *pbSupportMultiDown);\n    CURL_RET TryCheckIsSupportRange(const string&amp; strUrl, BOOL *pbSupportMultiDown);\n\n    CURLSH*                     m_sharedns_handle;\n    static CRITICAL_SECTION     m_csFileLock;\n};\n<\/code><\/pre>\n<p>\u4f7f\u7528\u65b9\u6cd5\uff1a<\/p>\n<pre><code class=\"language-c \">CurlDownload* pDown = CurlDownload::GetInstense();\nif (pDown != NULL)\n{\n    pDown-&gt;DownLoad(\"https:\/\/xxx.com\/1.exe\", \"c:\\\\2.exe\");\n}\n<\/code><\/pre>\n","protected":false},"excerpt":{"rendered":"<p>libcurl\u7248\u672c: curl-7.61.1 \u7279\u6027: \u591a\u7ebf\u7a0b\u4e0b\u8f7d\uff0c\u5e73\u5747\u901f\u5ea6\u6bd4\u5355\u7ebf\u7a0b\u5feb\u5f88\u591a \u5931\u8d25\u591a\u6b21\u91cd\u8bd5\uff0c\u80fd\u9002\u5e94 [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[2],"tags":[],"_links":{"self":[{"href":"http:\/\/www.sinkland.cn\/index.php?rest_route=\/wp\/v2\/posts\/197"}],"collection":[{"href":"http:\/\/www.sinkland.cn\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/www.sinkland.cn\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/www.sinkland.cn\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/www.sinkland.cn\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=197"}],"version-history":[{"count":6,"href":"http:\/\/www.sinkland.cn\/index.php?rest_route=\/wp\/v2\/posts\/197\/revisions"}],"predecessor-version":[{"id":203,"href":"http:\/\/www.sinkland.cn\/index.php?rest_route=\/wp\/v2\/posts\/197\/revisions\/203"}],"wp:attachment":[{"href":"http:\/\/www.sinkland.cn\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=197"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/www.sinkland.cn\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=197"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/www.sinkland.cn\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=197"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}