转载

libcurl的封装,支持同步异步请求,支持多线程下载,支持https

最近在做一个项目,需要用到http get post等

需求分析需要做到同步和异步,异步请求的返回以可选的回调通知的方式进行。

本人以Linux为例,一步一步的来实现。

  1. 配置并且编译libcurl
    我以在Linux底下的交叉编译举例。
    libcurl源码下载: http://curl.haxx.se/download.html
    配置libcurl支持https和zlib压缩,必须需要openssl和zlib库
    openssl库源码下载: http://www.openssl.org/source/。下载1.02a以上的版本,避开心脏出血漏洞。
    zlib源码下载:http://www.zlib.net/。下载最新版本代码。
    新建文件夹carbon。源码解压至目录carbon。
    1.1 配置openssl并且编译
    配置和编译脚本:
    libcurl的封装,支持同步异步请求,支持多线程下载,支持https
      1 #!/bin/bash   2 # Cross-compile environment for Android on ARMv7 and x86   3 #   4 # Contents licensed under the terms of the OpenSSL license   5 # http://www.openssl.org/source/license.html   6 #   7 # See http://wiki.openssl.org/index.php/FIPS_Library_and_Android   8 #   and http://wiki.openssl.org/index.php/Android   9   10 #####################################################################  11   12 # Set ANDROID_NDK_ROOT to you NDK location. For example,  13 # /opt/android-ndk-r8e or /opt/android-ndk-r9. This can be done in a  14 # login script. If ANDROID_NDK_ROOT is not specified, the script will  15 # try to pick it up with the value of _ANDROID_NDK_ROOT below. If  16 # ANDROID_NDK_ROOT is set, then the value is ignored.  17 # _ANDROID_NDK="android-ndk-r8e"  18 #_ANDROID_NDK="android-ndk-r9"  19 _ANDROID_NDK="android-ndk-r10"  20 ANDROID_NDK_ROOT=$HOME/ndk/android-ndk-r10d  21 # Set _ANDROID_EABI to the EABI you want to use. You can find the  22 # list in $ANDROID_NDK_ROOT/toolchains. This value is always used.  23 # _ANDROID_EABI="x86-4.6"  24 # _ANDROID_EABI="arm-linux-androideabi-4.6"  25 _ANDROID_EABI="arm-linux-androideabi-4.8"  26 export ROOTDIR="${PWD}"  27   28 # Set _ANDROID_ARCH to the architecture you are building for.  29 # This value is always used.  30 # _ANDROID_ARCH=arch-x86  31 _ANDROID_ARCH=arch-arm  32   33 # Set _ANDROID_API to the API you want to use. You should set it  34 # to one of: android-14, android-9, android-8, android-14, android-5  35 # android-4, or android-3. You can't set it to the latest (for  36 # example, API-17) because the NDK does not supply the platform. At  37 # Android 5.0, there will likely be another platform added (android-22?).  38 # This value is always used.  39 # _ANDROID_API="android-14"  40 # _ANDROID_API="android-18"  41 # _ANDROID_API="android-19"  42 _ANDROID_API="android-5"  43   44 #####################################################################  45   46 # If the user did not specify the NDK location, try and pick it up.  47 # We expect something like ANDROID_NDK_ROOT=/opt/android-ndk-r8e  48 # or ANDROID_NDK_ROOT=/usr/local/android-ndk-r8e.  49   50 if [ -z "$ANDROID_NDK_ROOT" ]; then  51   52   _ANDROID_NDK_ROOT=""  53   if [ -z "$_ANDROID_NDK_ROOT" ] && [ -d "/usr/local/$_ANDROID_NDK" ]; then  54     _ANDROID_NDK_ROOT="/usr/local/$_ANDROID_NDK"  55   fi  56   57   if [ -z "$_ANDROID_NDK_ROOT" ] && [ -d "/opt/$_ANDROID_NDK" ]; then  58     _ANDROID_NDK_ROOT="/opt/$_ANDROID_NDK"  59   fi  60   61   if [ -z "$_ANDROID_NDK_ROOT" ] && [ -d "$HOME/$_ANDROID_NDK" ]; then  62     _ANDROID_NDK_ROOT="$HOME/$_ANDROID_NDK"  63   fi  64   65   if [ -z "$_ANDROID_NDK_ROOT" ] && [ -d "$PWD/$_ANDROID_NDK" ]; then  66     _ANDROID_NDK_ROOT="$PWD/$_ANDROID_NDK"  67   fi  68   69   # If a path was set, then export it  70   if [ ! -z "$_ANDROID_NDK_ROOT" ] && [ -d "$_ANDROID_NDK_ROOT" ]; then  71     export ANDROID_NDK_ROOT="$_ANDROID_NDK_ROOT"  72   fi  73 fi  74   75 # Error checking  76 # ANDROID_NDK_ROOT should always be set by the user (even when not running this script)  77 # http://groups.google.com/group/android-ndk/browse_thread/thread/a998e139aca71d77  78 if [ -z "$ANDROID_NDK_ROOT" ] || [ ! -d "$ANDROID_NDK_ROOT" ]; then  79   echo "Error: ANDROID_NDK_ROOT is not a valid path. Please edit this script."  80   # echo "$ANDROID_NDK_ROOT"  81   # exit 1  82 fi  83   84 # Error checking  85 if [ ! -d "$ANDROID_NDK_ROOT/toolchains" ]; then  86   echo "Error: ANDROID_NDK_ROOT/toolchains is not a valid path. Please edit this script."  87   # echo "$ANDROID_NDK_ROOT/toolchains"  88   # exit 1  89 fi  90   91 # Error checking  92 if [ ! -d "$ANDROID_NDK_ROOT/toolchains/$_ANDROID_EABI" ]; then  93   echo "Error: ANDROID_EABI is not a valid path. Please edit this script."  94   # echo "$ANDROID_NDK_ROOT/toolchains/$_ANDROID_EABI"  95   # exit 1  96 fi  97   98 #####################################################################  99  100 # Based on ANDROID_NDK_ROOT, try and pick up the required toolchain. We expect something like: 101 # /opt/android-ndk-r83/toolchains/arm-linux-androideabi-4.7/prebuilt/linux-x86_64/bin 102 # Once we locate the toolchain, we add it to the PATH. Note: this is the 'hard way' of 103 # doing things according to the NDK documentation for Ice Cream Sandwich. 104 # https://android.googlesource.com/platform/ndk/+/ics-mr0/docs/STANDALONE-TOOLCHAIN.html 105  106 ANDROID_TOOLCHAIN="" 107 for host in "linux-x86_64" "linux-x86" "darwin-x86_64" "darwin-x86" 108 do 109   if [ -d "$ANDROID_NDK_ROOT/toolchains/$_ANDROID_EABI/prebuilt/$host/bin" ]; then 110     ANDROID_TOOLCHAIN="$ANDROID_NDK_ROOT/toolchains/$_ANDROID_EABI/prebuilt/$host/bin" 111     break 112   fi 113 done 114  115 # Error checking 116 if [ -z "$ANDROID_TOOLCHAIN" ] || [ ! -d "$ANDROID_TOOLCHAIN" ]; then 117   echo "Error: ANDROID_TOOLCHAIN is not valid. Please edit this script." 118   # echo "$ANDROID_TOOLCHAIN" 119   # exit 1 120 fi 121  122 case $_ANDROID_ARCH in 123     arch-arm)       124       ANDROID_TOOLS="arm-linux-androideabi-gcc arm-linux-androideabi-ranlib arm-linux-androideabi-ld" 125       ;; 126     arch-x86)       127       ANDROID_TOOLS="i686-linux-android-gcc i686-linux-android-ranlib i686-linux-android-ld" 128       ;;       129     *) 130       echo "ERROR ERROR ERROR" 131       ;; 132 esac 133  134 for tool in $ANDROID_TOOLS 135 do 136   # Error checking 137   if [ ! -e "$ANDROID_TOOLCHAIN/$tool" ]; then 138     echo "Error: Failed to find $tool. Please edit this script." 139     # echo "$ANDROID_TOOLCHAIN/$tool" 140     # exit 1 141   fi 142 done 143  144 # Only modify/export PATH if ANDROID_TOOLCHAIN good 145 if [ ! -z "$ANDROID_TOOLCHAIN" ]; then 146   export ANDROID_TOOLCHAIN="$ANDROID_TOOLCHAIN" 147   export PATH="$ANDROID_TOOLCHAIN":"$PATH" 148 fi 149  150 ##################################################################### 151  152 # For the Android SYSROOT. Can be used on the command line with --sysroot 153 # https://android.googlesource.com/platform/ndk/+/ics-mr0/docs/STANDALONE-TOOLCHAIN.html 154 export ANDROID_SYSROOT="$ANDROID_NDK_ROOT/platforms/$_ANDROID_API/$_ANDROID_ARCH" 155 export SYSROOT="$ANDROID_SYSROOT" 156 export NDK_SYSROOT="$ANDROID_SYSROOT" 157  158 # Error checking 159 if [ -z "$ANDROID_SYSROOT" ] || [ ! -d "$ANDROID_SYSROOT" ]; then 160   echo "Error: ANDROID_SYSROOT is not valid. Please edit this script." 161   # echo "$ANDROID_SYSROOT" 162   # exit 1 163 fi 164  165 ##################################################################### 166  167 # If the user did not specify the FIPS_SIG location, try and pick it up 168 # If the user specified a bad location, then try and pick it up too. 169 if [ -z "$FIPS_SIG" ] || [ ! -e "$FIPS_SIG" ]; then 170  171   # Try and locate it 172   _FIPS_SIG="" 173   if [ -d "/usr/local/ssl/$_ANDROID_API" ]; then 174     _FIPS_SIG=`find "/usr/local/ssl/$_ANDROID_API" -name incore` 175   fi 176  177   if [ ! -e "$_FIPS_SIG" ]; then 178     _FIPS_SIG=`find $PWD -name incore` 179   fi 180  181   # If a path was set, then export it 182   if [ ! -z "$_FIPS_SIG" ] && [ -e "$_FIPS_SIG" ]; then 183     export FIPS_SIG="$_FIPS_SIG" 184   fi 185 fi 186  187 # Error checking. Its OK to ignore this if you are *not* building for FIPS 188 if [ -z "$FIPS_SIG" ] || [ ! -e "$FIPS_SIG" ]; then 189   echo "Error: FIPS_SIG does not specify incore module. Please edit this script." 190   # echo "$FIPS_SIG" 191   # exit 1 192 fi 193  194 ##################################################################### 195  196 # Most of these should be OK (MACHINE, SYSTEM, ARCH). RELEASE is ignored. 197 export MACHINE=armv7 198 export RELEASE=2.6.37 199 export SYSTEM=android 200 export ARCH=arm 201 export CROSS_COMPILE="arm-linux-androideabi-" 202  203 if [ "$_ANDROID_ARCH" == "arch-x86" ]; then 204     export MACHINE=i686 205     export RELEASE=2.6.37 206     export SYSTEM=android 207     export ARCH=x86 208     export CROSS_COMPILE="i686-linux-android-" 209 fi 210  211 # For the Android toolchain 212 # https://android.googlesource.com/platform/ndk/+/ics-mr0/docs/STANDALONE-TOOLCHAIN.html 213 export ANDROID_SYSROOT="$ANDROID_NDK_ROOT/platforms/$_ANDROID_API/$_ANDROID_ARCH" 214 export SYSROOT="$ANDROID_SYSROOT" 215 export NDK_SYSROOT="$ANDROID_SYSROOT" 216 export ANDROID_NDK_SYSROOT="$ANDROID_SYSROOT" 217 export ANDROID_API="$_ANDROID_API" 218  219 # CROSS_COMPILE and ANDROID_DEV are DFW (Don't Fiddle With). Its used by OpenSSL build system. 220 # export CROSS_COMPILE="arm-linux-androideabi-" 221 export ANDROID_DEV="$ANDROID_NDK_ROOT/platforms/$_ANDROID_API/$_ANDROID_ARCH/usr" 222 export HOSTCC=gcc 223  224 VERBOSE=1 225 if [ ! -z "$VERBOSE" ] && [ "$VERBOSE" != "0" ]; then 226   echo "ANDROID_NDK_ROOT: $ANDROID_NDK_ROOT" 227   echo "ANDROID_ARCH: $_ANDROID_ARCH" 228   echo "ANDROID_EABI: $_ANDROID_EABI" 229   echo "ANDROID_API: $ANDROID_API" 230   echo "ANDROID_SYSROOT: $ANDROID_SYSROOT" 231   echo "ANDROID_TOOLCHAIN: $ANDROID_TOOLCHAIN" 232   echo "FIPS_SIG: $FIPS_SIG" 233   echo "CROSS_COMPILE: $CROSS_COMPILE" 234   echo "ANDROID_DEV: $ANDROID_DEV" 235 fi 236  237 cd openssl 238 if [ $# -gt 0 ]; then 239 perl -pi -e 's/install: all install_docs install_sw/install: install_docs install_sw/g' Makefile.org 240 ./config -DOPENSSL_NO_HEARTBEATS no-shared no-ssl2 no-ssl3 no-comp no-hw no-engine --openssldir=${ROOTDIR}/build/openssl 241 fi 242 make depend 243 make && make install
    openssl configure

    1.2 配置zlib并且编译配置脚本:

    libcurl的封装,支持同步异步请求,支持多线程下载,支持https
     1 #!/bin/sh  2   3 export ROOTDIR="${PWD}"  4 cd zlib/  5   6 export CROSS_COMPILE="arm-linux-androideabi"  7 export CPPFLAGS="-fPIC"  8 export CFLAGS="-fPIC"  9 export AR=${CROSS_COMPILE}-ar 10 export AS=${CROSS_COMPILE}-as 11 export LD=${CROSS_COMPILE}-ld 12 export RANLIB=${CROSS_COMPILE}-ranlib 13 export CC=${CROSS_COMPILE}-gcc 14 export CXX=${CROSS_COMPILE}-g++ 15 export NM=${CROSS_COMPILE}-nm 16  17 ./configure --prefix=${ROOTDIR}/build/zlib --static
    zlib configure

    配置成功之后,cd进代码目录执行make && make install命令即可

    1.3 配置libcurl并且编译

    配置脚本:
    libcurl的封装,支持同步异步请求,支持多线程下载,支持https
     1 #!/bin/sh  2   3 export ROOTDIR="${PWD}"  4 cd curl-7.42.1/  5   6 export CROSS_COMPILE="arm-linux-androideabi"  7 export CPPFLAGS="-fPIC -I${ROOTDIR}/build/openssl/include -I${ROOTDIR}/build/zlib/include"  8 export CFLAGS="-fPIC -I${ROOTDIR}/build/openssl/include -I${ROOTDIR}/build/zlib/include"  9  10 export LDFLAGS="-L${ROOTDIR}/build/openssl/lib -L${ROOTDIR}/build/zlib/lib" 11 export LIBS="-lssl -lcrypto -lz" 12  13 export AR=${CROSS_COMPILE}-ar 14 export AS=${CROSS_COMPILE}-as 15 export LD=${CROSS_COMPILE}-ld 16 export RANLIB=${CROSS_COMPILE}-ranlib 17 export CC=${CROSS_COMPILE}-gcc 18 export CXX=${CROSS_COMPILE}-g++ 19 export NM=${CROSS_COMPILE}-nm 20  21 ./configure --prefix=${ROOTDIR}/build/curl --target=${CROSS_COMPILE} --host=${CROSS_COMPILE} --build=i686-linux --enable-static=libcurl.a --enable-shared=libcurl.so --enable-symbol-hiding --enable-optimize --enable-ftp --enable-http --enable-file  --enable-proxy --enable-tftp --enable-smtp --enable-telnet --enable-cookies --enable-ipv6 --with-ssl --with-zlib --without-libssh2 --with-random=/dev/urandom
    libcurl configure

    配置成功之后,cd进代码目录执行make && make install命令即可

    本配置使用的是android的ndk工具链gcc 4.8
    在配置openssl时,指定了ANDROID_NDK_ROOT的值为ndk的路径,可以参看脚本的值进行对应的设置
    可以在ndk目录的build/tools目录找到make-standalone-toolchain.sh文件,执行make-standalone-toolchain.sh --help --help来查看帮助

    构建自己的ndk gcc工具链,最后将生成的工具链路径加入进环境变量PATH即可

  2. 封装libcurl库
    代码使用C++封装,并且使用了C++11的特性,编译时需要指定-std=c++11
    头文件:
    libcurl的封装,支持同步异步请求,支持多线程下载,支持https
      1 #ifndef __HTTP_REQUEST_H   2 #define __HTTP_REQUEST_H   3    4    5 #include <string>   6 #include <map>   7 #include <memory>   8 #include <functional>   9 #include <vector>  10   11 //************************************  12 // Usage:      13 // class MyResultClass  14 // {  15 // public:  16 //     MyResultClass() : m_request_finished(false) { }  17 //     ~MyResultClass() { }  18 //   19 // public:  20 //     void MyRequestResultCallback(int id, bool success, const std::string& data)  21 //     {  22 //       if (success)  23 //       {  24 //        std::ofstream outfile;  25 //        outfile.open("baidu.html", std::ios_base::binary | std::ios_base::trunc);  26 //        if (outfile.good()) outfile.write(data.c_str(), data.size());  27 //       }  28 //       m_request_finished = true;  29 //     }  30 //     bool IsRequestFinish(void) { return m_request_finished; }  31 // private:  32 //     bool m_request_finished;  33 // };  34 //  35 // MyResultClass mc;  36 // HttpRequest request;  37 // request.SetRequestUrl("http://www.baidu.com");  38 // request.SetResultCallback(std::bind(&MyResultClass::MyRequestResultCallback, &mc, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));  39 // request.SetRequestHeader("User-Agent:Mozilla/4.04[en](Win95;I;Nav)");  40 // HANDLE hRequest = request.PerformRequest(HttpRequest::REQUEST_ASYNC);  41 // if (hRequest)  42 // {  43 //     while (mc.IsRequestFinish() == false) Sleep(300);  44 //     long http_code;  45 //     if (request.GetHttpCode(hRequest, &http_code))  46 //       std::cout << "http code: " << http_code << std::endl;  47 //     std::string header;  48 //     if (request.GetReceiveHeader(hRequest, &header))  49 //       std::cout << header << std::endl;  50 //     HttpRequest::Close(hRequest);  51 // }  52 // /*recommended HttpRequest::Close(hRequest) while doing async request job and dont need request handle anymore*/  53 //************************************  54   55 class HttpLock;  56   57 #ifndef _WIN32  58 typedef void* HANDLE;  59 #endif  60   61 class HttpRequest  62 {  63 public:  64     typedef enum {  65         REQUEST_SYNC,  66         REQUEST_ASYNC,  67     }RequestType;  68   69     typedef enum {  70         REQUEST_OK,  71         REQUEST_INVALID_OPT,  72         REQUEST_PERFORM_ERROR,  73         REQUEST_OPENFILE_ERROR,  74         REQUEST_INIT_ERROR,  75     }RequestResult;  76   77     //int id, bool success, const std::string& data  78     typedef std::function<void(int, bool, const std::string&)> ResultCallback;  79   80     friend class HttpHelper;  81   82     HttpRequest();  83     ~HttpRequest();  84   85       86     int SetRetryTimes(int retry_times = s_kRetryCount);  87     int SetRequestId(int id);  88     int SetRequestTimeout(long time_out = 0);  89     int SetRequestUrl(const std::string& url);  90   91     //************************************  92     // Method:    SetMovedUrl  93     // FullName:  HttpRequest::SetMovedUrl  94     // Access:    public   95     // Returns:   int  96     // Description: set http redirect follow location  97     // Parameter: bool get_moved_url -- true means redirect http url  98     //************************************  99     int SetMovedUrl(bool get_moved_url); 100  101     int SetPostData(const std::string& message); 102     int SetPostData(const void* data, unsigned int size); 103  104     //************************************ 105     // Method:    SetRequestHeader 106     // FullName:  HttpRequest::SetRequestHeader 107     // Access:    public  108     // Returns:   int 109     // Description: set http request header, for example : Range:bytes=554554-  110     // Parameter: std::map<std::string, std::string>& 111     // Parameter: std::string> & headers 112     //************************************ 113     int SetRequestHeader(std::map<std::string, std::string>& headers); 114     int SetRequestHeader(const std::string& header); 115  116     int SetRequestProxy(const std::string& proxy, long proxy_port); 117  118  119     int SetResultCallback(ResultCallback rc); 120  121     HANDLE PerformRequest(RequestType request_type); 122     static void Close(HANDLE request_handle); 123  124     bool GetHttpCode(HANDLE request_handle, long* http_code); 125     bool GetReceiveHeader(HANDLE request_handle, std::string* header); 126     bool GetReceiveContent(HANDLE request_handle, std::string* receive); 127     bool GetErrorString(HANDLE request_handle, std::string* error_string); 128  129 protected: 130  131     class RequestHelper { 132     public: 133         RequestHelper(); 134         ~RequestHelper(); 135  136         friend class HttpRequest; 137         friend class HttpHelper; 138  139         int      SetRetryTimes(int retry_times) { m_retry_times = retry_times; return REQUEST_OK; } 140  141         int      SetRequestTimeout(long time_out = 0); 142         int      SetRequestUrl(const std::string& url); 143         int      SetMovedUrl(bool get_moved_url); 144         int      SetPostData(const void* data, unsigned int size); 145         int      SetRequestHeader(const std::string& header); 146         int      SetRequestProxy(const std::string& proxy, long proxy_port); 147  148         int      SetResultCallback(ResultCallback rc); 149  150         int      Perform(); 151  152         long     GetHttpCode() { return m_http_code; } 153         bool     GetHeader(std::string* header); 154         bool     GetContent(std::string* receive); 155         bool     GetErrorString(std::string* error_string); 156  157         bool     SelfClose(void) { return m_close_self; } 158  159     protected: 160         void    ReqeustResultDefault(int id, bool success, const std::string& data); 161  162     private: 163         HANDLE       m_curl_handle; 164         HANDLE       m_http_headers; 165 #ifdef _WIN32 166         HANDLE       m_perform_thread; 167 #else 168         pthread_t    m_perform_thread; 169 #endif 170  171         int         m_retry_times; 172         int         m_id; 173         bool        m_close_self; 174         bool        m_is_running; 175         long        m_http_code; 176  177         std::string     m_receive_content; 178         std::string     m_receive_header; 179         std::string     m_error_string; 180         char*               m_post_data; 181  182         ResultCallback  m_result_callback; 183     }; 184  185 private: 186     std::shared_ptr<RequestHelper>  m_request_handle; 187     static const int               s_kRetryCount = 3; 188 }; 189  190 //************************************ 191 // Usage:    HttpDownloader 192 // class DownCallbackClass 193 // { 194 // public: 195 //     DownCallbackClass() :m_down_finished(false) {} 196 //     ~DownCallbackClass() {} 197 // public: 198 //     void DownResultCallback(int id, bool success, const std::string& data) 199 //     { 200 //       m_down_finished = true; 201 //     } 202 //     int down_callback(double total_size, double downloaded_size, void* userdata) 203 //     { 204 //       long tmp = static_cast<long>(downloaded_size / total_size * 100); 205 //      printf("/r下载进度%d", tmp); 206 //      return 0; 207 //     } 208 //     bool IsDownFinished(void) { return m_down_finished; } 209 // private: 210 //     bool m_down_finished; 211 // }; 212 // HttpDownloader download; 213 // DownCallbackClass dc; 214 // const char* down_url = "http://dlsw.baidu.com/sw-search-sp/soft/71/10998/OfflineBaiduPlayer_151_V4.1.2.263.1432003947.exe"; 215 // const char* down_file = "BaiduPlayer.exe"; 216 //  217 // download.SetDownloadUrl(down_url); 218 // download.SetProgressCallback(std::bind(&DownCallbackClass::down_callback, &dc, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); 219 // download.SetResultCallback(std::bind(&DownCallbackClass::DownResultCallback, &dc, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); 220 // download.DownloadFile(down_file); 221 // HANDLE hDownload = download.StartDownload(HttpDownloader::DOWN_ASYNC); 222 // if (hDownload) 223 // { 224 //     while (dc.IsDownFinished() == false) Sleep(300);  225 //     //to do download finish clean up 226 //     HttpDownloader::Close(hDownload); 227 // } 228 //************************************ 229  230 class HttpDownloader 231 { 232 public: 233     typedef enum { 234         DOWN_SYNC, 235         DOWN_ASYNC, 236     }DownType; 237  238     //double total_size, double downloaded_size, void* userdata 239     typedef std::function<int(double, double, void*)> ProgressCallback; 240     //int id, bool success, const std::string& data 241     typedef std::function<void(int, bool, const std::string&)> ResultCallback; 242  243     friend class HttpHelper; 244  245     HttpDownloader(); 246     ~HttpDownloader(); 247  248     int         SetRequestProxy(const std::string& proxy, long proxy_port); 249     int         SetRetryTimes(int retry_times = s_kRetryCount); 250     int         SetTimeout(long time_out = 0); 251     int         SetDownloadUrl(const std::string& url); 252     int         SetUserData(void* userdata); 253     int         SetRequestId(int id); 254     int         SetProgressCallback(ProgressCallback pc); 255     int         SetResultCallback(ResultCallback rc); 256  257     int         DownloadFile(const std::string& file_name, int thread_count = 5); 258     HANDLE      StartDownload(DownType down_type); 259     static bool CancelDownload(HANDLE handle); 260     static void Close(HANDLE handle); 261  262     bool        GetHttpCode(HANDLE handle, long* http_code); 263     bool        GetReceiveHeader(HANDLE handle, std::string* header); 264     bool        GetErrorString(HANDLE handle, std::string* error_string); 265     void*       GetUserData(HANDLE handle); 266  267 protected: 268  269     class DownloadHelper { 270     public: 271         typedef struct tThreadChunk 272         { 273             FILE*       _fp; 274             long        _startidx; 275             long        _endidx; 276  277             DownloadHelper*     _download; 278         }ThreadChunk; 279  280         DownloadHelper(); 281         ~DownloadHelper(); 282  283         friend class HttpDownloader; 284         friend class HttpHelper; 285         friend ThreadChunk; 286  287         void     SetRetryTimes(int retry_times) { m_retry_times = retry_times; } 288         void      SetRequestId(int id) { m_id = id;  } 289         int      SetTimeout(long time_out = 0); 290         int      SetRequestUrl(const std::string& url); 291         int      SetRequestProxy(const std::string& proxy, long proxy_port); 292  293         void     SetUserData(void *userdata) { m_userdata = userdata; } 294         int      SetProgressCallback(ProgressCallback pc); 295         int      SetResultCallback(ResultCallback rc); 296         int      SetDownloadFile(const std::string& file_name); 297         int      SetDownloadThreadCount(int thread_count); 298  299         int      Perform(); 300  301         int      GetHttpCode() { return m_http_code; } 302         bool     GetHeader(std::string* header); 303         bool     GetErrorString(std::string* error_string); 304         bool     SelfClose(void) { return m_close_self; } 305         void*    GetUserData(void) { return m_userdata; } 306  307     protected: 308         int      DownloadDefaultCallback(double total_size, double downloaded_size, void* userdata); 309         void     ResultDefaultCallback(int id, bool success, const std::string& data); 310         double   GetDownloadFileSize(); 311         int      DoDownload(ThreadChunk* thread_chunk); 312         int      SplitDownloadCount(double down_size); 313  314     private: 315 #ifdef _WIN32 316         HANDLE        m_perform_thread; 317 #else 318         pthread_t     m_perform_thread; 319 #endif 320  321         int          m_retry_times; 322         int          m_thread_count; 323         int          m_id; 324         long         m_time_out; 325  326         std::string  m_file_path; 327         std::string  m_url; 328         std::string  m_http_proxy; 329         std::string  m_receive_header; 330         std::string  m_error_string; 331  332         bool          m_close_self; 333         bool            m_multi_download; 334         bool         m_download_fail; 335         bool          m_is_running; 336         bool         m_is_cancel; 337         void*        m_userdata; 338         long         m_http_code; 339         long         m_proxy_port; 340         double       m_total_size; 341         double       m_downloaded_size; 342  343         std::shared_ptr<HttpLock> m_httplock; 344         ProgressCallback  m_download_callback; 345         ResultCallback    m_result_callback; 346     }; 347  348 private: 349     std::shared_ptr<DownloadHelper>    m_request_handle; 350  351     static const int          s_kRetryCount = 3; 352     static const int          s_kThreadCount = 4; 353 }; 354  355 #endif  /*__HTTP_REQUEST_H*/
    HttpRequest.h

    实现文件:

    libcurl的封装,支持同步异步请求,支持多线程下载,支持https
       1 //created by carbon @ 2015-05-29    2 /*    3                    _ooOoo_    4                   o8888888o    5                   88" . "88    6                   (| -_- |)    7                   O/  =  /O    8                 ___/`---'/____    9             .'  //|      |//  `.   10             /  //|||  :  |||//  /   11            /  _||||| -:- |||||-  /   12            |   | ///  -  /// |   |   13            | /_|  ''/---/''  |   |   14            /  .-/__  `-`  ___/-. /   15          ___`. .'  /--.--/  `. . __   16       ."" '<  `.___/_<|>_/___.'  >'"".   17      | | :  `- /`.;`/ _ /`;.`/ - ` : | |   18      /  / `-.   /_ __/ /__ _/   .-` /  /   19 ======`-.____`-.___/_____/___.-`____.-'======   20                    `=---='   21 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^   22      佛祖保佑    永无BUG   23 */   24 #ifdef _WIN32   25 #include "stdafx.h"   26 #else   27 #include <pthread.h>   28 #include <stdio.h>   29 #include <unistd.h>   30 #endif   31    32 #include "HttpRequest.h"   //HttpRequest class   33 #include "curl/curl.h"    //libcurl interface   34    35 #include <list>   36 #include <regex>   37 #include <sstream>   38    39    40 #ifndef _WIN32   41 typedef unsigned long DWORD;   42 #define INVALID_HANDLE_VALUE    (void*)0xffffffff   43 #define TRUE    1   44 #define FALSE   0   45 #endif  //#ifndef _WIN32   46    47 class HttpLock   48 {   49 public:   50 #ifdef _WIN32   51     HttpLock() { InitializeCriticalSection(&_cs); }   52     ~HttpLock() { DeleteCriticalSection(&_cs); }   53    54     void Lock() { EnterCriticalSection(&_cs); }   55     void UnLock() { LeaveCriticalSection(&_cs); }   56 #else   57     HttpLock() { pthread_mutex_init(&_lock, NULL); }   58     ~HttpLock() { pthread_mutex_destroy(&_lock); }   59    60     int Lock(){ return pthread_mutex_lock(&_lock); }   61     int UnLock() { return pthread_mutex_unlock(&_lock); }   62 #endif   63    64 private:   65 #ifdef _WIN32   66     CRITICAL_SECTION _cs;   67 #else   68     pthread_mutex_t    _lock;   69 #endif   70 };   71    72 class HttpHelper {   73 protected:   74     HttpHelper()   75     {   76         curl_global_init(CURL_GLOBAL_DEFAULT);   77    78         s_share_handle = curl_share_init();   79         curl_share_setopt(s_share_handle, CURLSHOPT_SHARE, CURL_LOCK_DATA_DNS);   80     }   81    82 public:   83     ~HttpHelper()   84     {   85         curl_share_cleanup(s_share_handle);   86         curl_global_cleanup();   87    88         s_async_requests.clear();   89         s_async_downloads.clear();   90     }   91    92     static HttpHelper& Instance()   93     {   94         static HttpHelper the_single_instance;   95         s_id++;   96         return the_single_instance;   97     }   98    99     static void set_share_handle(CURL* curl_handle)  100     {  101         curl_easy_setopt(curl_handle, CURLOPT_SHARE, s_share_handle);  102         curl_easy_setopt(curl_handle, CURLOPT_DNS_CACHE_TIMEOUT, 60 * 5);  103     }  104   105     static std::list< std::shared_ptr<HttpRequest::RequestHelper> > s_async_requests;  106     static std::list< std::shared_ptr<HttpDownloader::DownloadHelper> > s_async_downloads;  107   108     static int s_id;  109     static HttpLock       s_request_lock;  110     static HttpLock       s_download_lock;  111     static CURLSH*        s_share_handle;  112   113 #ifdef _WIN32  114     static DWORD WINAPI RequestThread(LPVOID param)  115 #else  116     static void* RequestThread(void* param)  117 #endif  118     {  119 #ifdef _WIN32  120         Sleep(10);  121 #else  122         usleep(10 * 1000);  123 #endif  124   125         std::shared_ptr<HttpRequest::RequestHelper>* request = reinterpret_cast<std::shared_ptr<HttpRequest::RequestHelper>*>(param);  126   127         if (request)  128         {  129             (*request)->Perform();  130             if ((*request)->SelfClose())  131             {  132                 s_request_lock.Lock();  133                 HttpHelper::s_async_requests.remove(*request);  134                 s_request_lock.UnLock();  135             }  136   137         }  138   139 #ifdef _WIN32  140         return 1;  141 #else  142         return NULL;  143 #endif  144     }  145   146     static size_t RetriveHeaderFunction(void *ptr, size_t size, size_t nmemb, void *stream)  147     {  148         std::string* receive_header = reinterpret_cast<std::string*>(stream);  149         if (receive_header && ptr)  150         {  151             receive_header->append(reinterpret_cast<const char*>(ptr), size * nmemb);  152         }  153   154         return nmemb * size;  155     }  156   157     static size_t RetriveContentFunction(void *ptr, size_t size, size_t nmemb, void *stream)  158     {  159         std::string* receive_content = reinterpret_cast<std::string*>(stream);  160         if (receive_content && ptr)  161         {  162             receive_content->append(reinterpret_cast<const char*>(ptr), size * nmemb);  163         }  164   165         return nmemb * size;  166     }  167   168 #ifdef _WIN32  169     static DWORD WINAPI DownloadThread(LPVOID param)  170 #else  171     static void* DownloadThread(void* param)  172 #endif  173     {  174 #ifdef _WIN32  175         Sleep(10);  176 #else  177         usleep(10 * 1000);  178 #endif  179   180         std::shared_ptr<HttpDownloader::DownloadHelper>* request = reinterpret_cast<std::shared_ptr<HttpDownloader::DownloadHelper>*>(param);  181   182         if (request)  183         {  184             (*request)->Perform();  185   186             if ((*request)->SelfClose())  187             {  188                 s_download_lock.Lock();  189                 HttpHelper::s_async_downloads.remove(*request);  190                 s_download_lock.UnLock();  191             }  192   193         }  194   195 #ifdef _WIN32  196         return 1;  197 #else  198         return NULL;  199 #endif  200     }  201   202     static size_t write_callback(char *ptr, size_t size, size_t nmemb, void *userdata)  203     {  204         HttpDownloader::DownloadHelper::ThreadChunk* thread_chunk = reinterpret_cast<HttpDownloader::DownloadHelper::ThreadChunk*>(userdata);  205   206         if (thread_chunk->_download->m_is_cancel)  207         {  208             return 0;  209         }  210   211         thread_chunk->_download->m_httplock->Lock();  212         size_t written = 0;  213         if (thread_chunk->_startidx <= thread_chunk->_endidx)  214         {  215             int real_size = size * nmemb;  216             if (thread_chunk->_startidx + real_size > thread_chunk->_endidx)  217             {  218                 real_size = thread_chunk->_endidx - thread_chunk->_startidx + 1;  219             }  220   221             if (fseek(thread_chunk->_fp, thread_chunk->_startidx, SEEK_SET) != 0)  222             {  223                 perror("fseek");  224             }  225             else  226             {  227                 written = fwrite(ptr, 1, real_size, thread_chunk->_fp);  228                 thread_chunk->_startidx += written;  229             }  230             thread_chunk->_download->m_downloaded_size += written;  231         }  232         thread_chunk->_download->m_httplock->UnLock();  233   234         return written;  235     }  236   237     static int progress_callback(void *clientp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow)  238     {  239         HttpDownloader::DownloadHelper::ThreadChunk* thread_chunk = reinterpret_cast<HttpDownloader::DownloadHelper::ThreadChunk*>(clientp);  240   241         thread_chunk->_download->m_httplock->Lock();  242   243         double total_size = thread_chunk->_download->m_total_size;  244         double downloaded_size = thread_chunk->_download->m_downloaded_size;  245         void* userdata = thread_chunk->_download->m_userdata;  246         int callback_result = thread_chunk->_download->m_download_callback(total_size, downloaded_size, userdata);  247   248         thread_chunk->_download->m_httplock->UnLock();  249   250         return callback_result;  251     }  252   253 #ifdef _WIN32  254     static DWORD WINAPI DownloadWork(LPVOID param)  255 #else  256     static void* DownloadWork(void* param)  257 #endif  258     {  259         HttpDownloader::DownloadHelper::ThreadChunk* thread_chunk = reinterpret_cast<HttpDownloader::DownloadHelper::ThreadChunk*>(param);  260   261 #ifdef _WIN32  262         return thread_chunk->_download->DoDownload(thread_chunk);  263 #else  264         return (void *)(thread_chunk->_download->DoDownload(thread_chunk));  265 #endif  266     }  267 };  268   269 std::list< std::shared_ptr<HttpRequest::RequestHelper> > HttpHelper::s_async_requests;  270 std::list< std::shared_ptr<HttpDownloader::DownloadHelper> > HttpHelper::s_async_downloads;  271 int HttpHelper::s_id = 0;  272 HttpLock HttpHelper::s_request_lock;  273 HttpLock HttpHelper::s_download_lock;  274 CURLSH* HttpHelper::s_share_handle = nullptr;  275   276 HttpRequest::HttpRequest()  277     : m_request_handle(new HttpRequest::RequestHelper)  278 {  279     HttpHelper::Instance();  280 }  281   282 HttpRequest::~HttpRequest()  283 {  284 }  285   286 int HttpRequest::SetRetryTimes(int retry_times)  287 {  288     if (m_request_handle)  289     {  290         m_request_handle->SetRetryTimes(retry_times);  291         return REQUEST_OK;  292     }  293   294     return REQUEST_INIT_ERROR;  295 }  296   297 int HttpRequest::SetRequestId(int id)  298 {  299     if (m_request_handle)  300     {  301         m_request_handle->m_id = id;  302         return REQUEST_OK;  303     }  304   305     return REQUEST_INIT_ERROR;  306 }  307   308 int HttpRequest::SetRequestTimeout(long time_out)  309 {  310     if (m_request_handle)  311     {  312         if (m_request_handle->SetRequestTimeout(time_out) == CURLE_OK)  313         {  314             return REQUEST_OK;  315         }  316         else  317         {  318             return REQUEST_INVALID_OPT;  319         }  320     }  321   322     return REQUEST_INIT_ERROR;  323 }  324   325 int HttpRequest::SetRequestUrl(const std::string& url)  326 {  327     if (m_request_handle)  328     {  329         if (m_request_handle->SetRequestUrl(url) == CURLE_OK)  330         {  331             return REQUEST_OK;  332         }  333         else  334         {  335             return REQUEST_INVALID_OPT;  336         }  337     }  338   339     return REQUEST_INIT_ERROR;  340 }  341   342 int HttpRequest::SetMovedUrl(bool get_moved_url)  343 {  344     if (m_request_handle)  345     {  346         if (m_request_handle->SetMovedUrl(get_moved_url) == CURLE_OK)  347         {  348             return REQUEST_OK;  349         }  350         else  351         {  352             return REQUEST_INVALID_OPT;  353         }  354     }  355   356     return REQUEST_INIT_ERROR;  357 }  358   359 int HttpRequest::SetPostData(const std::string& message)  360 {  361     return SetPostData(message.c_str(), message.size());  362 }  363   364 int HttpRequest::SetPostData(const void* data, unsigned int size)  365 {  366     if (m_request_handle)  367     {  368         if (m_request_handle->SetPostData(data, size) == CURLE_OK)  369         {  370             return REQUEST_OK;  371         }  372         else  373         {  374             return REQUEST_INVALID_OPT;  375         }  376     }  377     return REQUEST_INIT_ERROR;  378 }  379   380 int HttpRequest::SetRequestHeader(std::map<std::string, std::string>& headers)  381 {  382     if (m_request_handle)  383     {  384         for (auto it = headers.begin(); it != headers.end(); ++it)  385         {  386             std::string header = it->first;  387             header += ": ";  388             header += it->second;  389             if (m_request_handle->SetRequestHeader(header) != CURLE_OK)  390             {  391                 return REQUEST_INVALID_OPT;  392             }  393         }  394         return REQUEST_OK;  395     }  396   397     return REQUEST_INIT_ERROR;  398 }  399   400 int HttpRequest::SetRequestHeader(const std::string& header)  401 {  402     if (m_request_handle)  403     {  404         if (m_request_handle->SetRequestHeader(header) == CURLE_OK)  405         {  406             return REQUEST_OK;  407         }  408         else  409         {  410             return REQUEST_INVALID_OPT;  411         }  412     }  413     return REQUEST_INIT_ERROR;  414 }  415   416 int HttpRequest::SetRequestProxy(const std::string& proxy, long proxy_port)  417 {  418     if (m_request_handle)  419     {  420         if (m_request_handle->SetRequestProxy(proxy, proxy_port) == CURLE_OK)  421         {  422             return REQUEST_OK;  423         }  424         else  425         {  426             return REQUEST_INVALID_OPT;  427         }  428     }  429   430     return REQUEST_INIT_ERROR;  431 }  432   433 int HttpRequest::SetResultCallback(ResultCallback rc)  434 {  435     if (m_request_handle)  436     {  437         m_request_handle->SetResultCallback(rc);  438         return REQUEST_OK;  439     }  440   441     return REQUEST_INIT_ERROR;  442 }  443   444 void HttpRequest::Close(HANDLE request_handle)  445 {  446     std::shared_ptr<RequestHelper>* request = (reinterpret_cast<std::shared_ptr<RequestHelper> *>(request_handle));  447     if (request == INVALID_HANDLE_VALUE || request == nullptr)  448     {  449         return;  450     }  451   452     bool basync = false;  453   454     HttpHelper::s_request_lock.Lock();  455     for (auto it = HttpHelper::s_async_requests.begin(); it != HttpHelper::s_async_requests.end(); ++it)  456     {  457         if ((*request) == *it)  458         {  459 #ifdef _WIN32  460             if (WaitForSingleObject((*request)->m_perform_thread, 10) == WAIT_OBJECT_0)  461 #else  462             if(pthread_kill((*request)->m_perform_thread, 0) != 0)  463 #endif  464             {  465                 HttpHelper::s_async_requests.remove(*request);  466             }  467             else  468             {  469                 (*request)->m_close_self = true;  470             }  471             basync = true;  472             break;  473         }  474     }  475     HttpHelper::s_request_lock.UnLock();  476   477     if (basync == false)  478     {  479         //request->reset();  480     }  481 }  482   483 HANDLE HttpRequest::PerformRequest(RequestType request_type)  484 {  485     if (m_request_handle)  486     {  487         if (m_request_handle->m_is_running)  488         {  489             return nullptr;  490         }  491   492         if (request_type == REQUEST_SYNC)  493         {  494             m_request_handle->Perform();  495   496             return &m_request_handle;  497         }  498         else if (request_type == REQUEST_ASYNC)  499         {  500             HttpHelper::s_request_lock.Lock();  501             HttpHelper::s_async_requests.push_back(m_request_handle);  502             std::shared_ptr<RequestHelper>& request = HttpHelper::s_async_requests.back();  503   504 #ifdef _WIN32  505             DWORD thread_id;  506             HANDLE async_thread = CreateThread(NULL, 0, HttpHelper::RequestThread, &request, 0, &thread_id);  507             request->m_perform_thread = async_thread;  508 #else  509             pthread_create(&(request->m_perform_thread), NULL, HttpHelper::RequestThread, &request);  510 #endif  511             HttpHelper::s_request_lock.UnLock();  512   513             return &request;  514         }  515   516         return nullptr;  517     }  518   519     return nullptr;  520 }  521   522 bool HttpRequest::GetHttpCode(HANDLE request_handle, long* http_code)  523 {  524     std::shared_ptr<RequestHelper>* request = reinterpret_cast<std::shared_ptr<RequestHelper>*>(request_handle);  525     if (request && http_code)  526     {  527         *http_code = (*request)->GetHttpCode();  528         return true;  529     }  530   531     return false;  532 }  533   534 bool HttpRequest::GetReceiveHeader(HANDLE request_handle, std::string* header)  535 {  536     std::shared_ptr<RequestHelper>* request = reinterpret_cast<std::shared_ptr<RequestHelper>*>(request_handle);  537     if (request)  538     {  539         return (*request)->GetHeader(header);  540     }  541   542     return false;  543 }  544   545 bool HttpRequest::GetReceiveContent(HANDLE request_handle, std::string* receive)  546 {  547     std::shared_ptr<RequestHelper>* request = reinterpret_cast<std::shared_ptr<RequestHelper>*>(request_handle);  548     if (request)  549     {  550         return (*request)->GetContent(receive);  551     }  552   553     return false;  554 }  555   556 bool HttpRequest::GetErrorString(HANDLE request_handle, std::string* error_string)  557 {  558     std::shared_ptr<RequestHelper>* request = reinterpret_cast<std::shared_ptr<RequestHelper>*>(request_handle);  559     if (request)  560     {  561         return (*request)->GetErrorString(error_string);  562     }  563   564     return false;  565 }  566   567 HttpRequest::RequestHelper::RequestHelper()  568     : m_curl_handle(nullptr)  569 #ifdef _WIN32  570     , m_perform_thread(nullptr)  571 #else  572     , m_perform_thread(-1)  573 #endif  574     , m_http_headers(nullptr)  575     , m_close_self(false)  576     , m_is_running(false)  577     , m_retry_times(HttpRequest::s_kRetryCount)  578     , m_http_code(0)  579     , m_post_data(nullptr)  580 {  581     m_result_callback = std::bind(&RequestHelper::ReqeustResultDefault, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);  582     m_id = HttpHelper::s_id;  583     m_curl_handle = curl_easy_init();  584     HttpHelper::set_share_handle(m_curl_handle);  585 }  586   587 HttpRequest::RequestHelper::~RequestHelper()  588 {  589     if (m_curl_handle)  590     {  591         curl_easy_cleanup(m_curl_handle);  592     }  593     if (m_http_headers)  594     {  595         curl_slist_free_all(reinterpret_cast<curl_slist*>(m_http_headers));  596     }  597     if (m_post_data)  598     {  599         delete m_post_data;  600         m_post_data = nullptr;  601     }  602 #ifdef _WIN32  603     if (m_perform_thread)  604     {  605         CloseHandle(m_perform_thread);  606     }  607 #endif  608 }  609   610 int HttpRequest::RequestHelper::SetRequestTimeout(long time_out)  611 {  612     if (m_curl_handle)  613     {  614         return curl_easy_setopt(m_curl_handle, CURLOPT_TIMEOUT, 0);  615     }  616   617     return CURLE_FAILED_INIT;  618 }  619   620 int HttpRequest::RequestHelper::SetRequestUrl(const std::string& url)  621 {  622     if (m_curl_handle)  623     {  624         if (url.substr(0, 5) == "https")  625         {  626             curl_easy_setopt(m_curl_handle, CURLOPT_SSL_VERIFYPEER, 0L);  627             curl_easy_setopt(m_curl_handle, CURLOPT_SSL_VERIFYHOST, 0L);  628         }  629         return curl_easy_setopt(m_curl_handle, CURLOPT_URL, url.c_str());  630     }  631   632     return CURLE_FAILED_INIT;  633 }  634   635 int HttpRequest::RequestHelper::SetMovedUrl(bool get_moved_url)  636 {  637     if (m_curl_handle)  638     {  639         if (get_moved_url)  640         {  641             curl_easy_setopt(m_curl_handle, CURLOPT_MAXREDIRS, 5);  642             return curl_easy_setopt(m_curl_handle, CURLOPT_FOLLOWLOCATION, 1L);  643         }  644         else  645         {  646             return curl_easy_setopt(m_curl_handle, CURLOPT_FOLLOWLOCATION, 0L);  647         }  648     }  649   650     return CURLE_FAILED_INIT;  651 }  652   653 int HttpRequest::RequestHelper::SetPostData(const void* data, unsigned int size)  654 {  655     if (m_curl_handle && data && size > 0)  656     {  657         CURLcode curl_code = curl_easy_setopt(m_curl_handle, CURLOPT_POST, 1);  658         if (curl_code == CURLE_OK)  659         {  660             if (m_post_data)  661             {  662                 delete m_post_data;  663                 m_post_data = nullptr;  664             }  665             m_post_data = new char[size];  666             memcpy(m_post_data, data, size);  667             curl_code = curl_easy_setopt(m_curl_handle, CURLOPT_POSTFIELDS, m_post_data);  668         }  669   670         if (curl_code == CURLE_OK)  671         {  672             curl_code = curl_easy_setopt(m_curl_handle, CURLOPT_POSTFIELDSIZE, size);  673         }  674   675         return curl_code;  676     }  677   678     return CURLE_FAILED_INIT;  679 }  680   681 int HttpRequest::RequestHelper::SetRequestHeader(const std::string& header)  682 {  683     if (m_curl_handle && header.empty() == false)  684     {  685         m_http_headers = curl_slist_append(reinterpret_cast<curl_slist*>(m_http_headers), header.c_str());  686   687         return m_http_headers ? CURLE_OK : CURLE_FAILED_INIT;  688     }  689   690     return CURLE_FAILED_INIT;  691 }  692   693 int HttpRequest::RequestHelper::SetRequestProxy(const std::string& proxy, long proxy_port)  694 {  695     //CURLOPT_PROXY  696     if (m_curl_handle)  697     {  698         CURLcode curl_code = curl_easy_setopt(m_curl_handle, CURLOPT_PROXYPORT, proxy_port);  699   700         curl_code = curl_easy_setopt(m_curl_handle, CURLOPT_PROXY, proxy.c_str());  701   702         return curl_code;  703     }  704   705     return CURLE_FAILED_INIT;  706 }  707   708 int HttpRequest::RequestHelper::SetResultCallback(ResultCallback rc)  709 {  710     m_result_callback = rc;  711   712     return CURLE_OK;  713 }  714   715 void HttpRequest::RequestHelper::ReqeustResultDefault(int id, bool success, const std::string& data)  716 {  717     //default request callback do nothing  718 }  719   720 int HttpRequest::RequestHelper::Perform()  721 {  722     if (m_curl_handle)  723     {  724         CURLcode curl_code;  725         if (m_http_headers)  726         {  727             curl_code = curl_easy_setopt(m_curl_handle, CURLOPT_HTTPHEADER, reinterpret_cast<curl_slist*>(m_http_headers));  728             if (curl_code != CURLE_OK)  729             {  730                 return curl_code;  731             }  732         }  733   734         m_is_running = true;  735         m_receive_header.clear();  736         m_receive_content.clear();  737   738         //set force http redirect  739         SetMovedUrl(true);  740   741         curl_code = curl_easy_setopt(m_curl_handle, CURLOPT_HEADERFUNCTION, HttpHelper::RetriveHeaderFunction);  742         curl_code = curl_easy_setopt(m_curl_handle, CURLOPT_HEADERDATA, &m_receive_header);  743   744         curl_code = curl_easy_setopt(m_curl_handle, CURLOPT_WRITEFUNCTION, HttpHelper::RetriveContentFunction);  745         curl_code = curl_easy_setopt(m_curl_handle, CURLOPT_WRITEDATA, &m_receive_content);  746   747         curl_code = curl_easy_setopt(m_curl_handle, CURLOPT_NOPROGRESS, 1);  748   749         curl_code = curl_easy_setopt(m_curl_handle, CURLOPT_NOSIGNAL, 1);  750         curl_code = curl_easy_setopt(m_curl_handle, CURLOPT_CONNECTTIMEOUT_MS, 0);  751   752         curl_code = curl_easy_perform(m_curl_handle);  753         if (curl_code == CURLE_OPERATION_TIMEDOUT)  754         {  755             int retry_count = m_retry_times;  756             while (retry_count > 0)  757             {  758                 curl_code = curl_easy_perform(m_curl_handle);  759                 if (curl_code != CURLE_OPERATION_TIMEDOUT) break;  760                 retry_count--;  761             }  762         }  763   764         curl_easy_getinfo(m_curl_handle, CURLINFO_RESPONSE_CODE, &m_http_code);  765         if (curl_code == CURLE_OK && m_http_code == 200)  766         {  767             m_result_callback(m_id, true, m_receive_content);  768         }  769         else  770         {  771             const char* err_string = curl_easy_strerror(curl_code);  772             m_error_string = err_string;  773             curl_code = CURLE_HTTP_POST_ERROR;  774             m_result_callback(m_id, false, m_receive_content);  775         }  776   777         m_is_running = false;  778   779         if (m_http_headers)  780         {  781             curl_slist_free_all(reinterpret_cast<curl_slist*>(m_http_headers));  782             m_http_headers = nullptr;  783         }  784   785         return curl_code;  786     }  787   788     return CURLE_FAILED_INIT;  789 }  790   791 bool HttpRequest::RequestHelper::GetHeader(std::string* header)  792 {  793     if (m_receive_header.empty()) return false;  794     else if (header) *header = m_receive_header;  795   796     return true;  797 }  798   799 bool HttpRequest::RequestHelper::GetContent(std::string* receive)  800 {  801     if (m_receive_content.empty()) return false;  802     else if (receive) *receive = m_receive_content;  803   804     return true;  805 }  806   807 bool HttpRequest::RequestHelper::GetErrorString(std::string* error_string)  808 {  809     if (m_error_string.empty()) return false;  810     else if (error_string) *error_string = m_error_string;  811   812     return true;  813 }  814   815 HttpDownloader::HttpDownloader()  816     :m_request_handle(new HttpDownloader::DownloadHelper)  817 {  818     HttpHelper::Instance();  819 }  820   821 HttpDownloader::~HttpDownloader()  822 {  823   824 }  825   826 int HttpDownloader::SetRequestProxy(const std::string& proxy, long proxy_port)  827 {  828     if (m_request_handle)  829     {  830         if (m_request_handle->SetRequestProxy(proxy, proxy_port) == CURLE_OK)  831         {  832             return 0;  833         }  834         else  835         {  836             return HttpRequest::REQUEST_INVALID_OPT;  837         }  838     }  839   840     return HttpRequest::REQUEST_INIT_ERROR;  841 }  842   843 int HttpDownloader::SetRetryTimes(int retry_times /* = s_kRetryCount */)  844 {  845     if (m_request_handle)  846     {  847         m_request_handle->SetRetryTimes(retry_times);  848         return HttpRequest::REQUEST_OK;  849     }  850   851     return HttpRequest::REQUEST_INIT_ERROR;  852 }  853   854 int HttpDownloader::SetTimeout(long time_out /* = 0 */)  855 {  856     if (m_request_handle)  857     {  858         if (m_request_handle->SetTimeout(time_out) == CURLE_OK)  859         {  860             return HttpRequest::REQUEST_OK;  861         }  862         else  863         {  864             return HttpRequest::REQUEST_INVALID_OPT;  865         }  866     }  867   868     return HttpRequest::REQUEST_INIT_ERROR;  869 }  870   871 int HttpDownloader::SetDownloadUrl(const std::string& url)  872 {  873     if (m_request_handle)  874     {  875         if (m_request_handle->SetRequestUrl(url) == CURLE_OK)  876         {  877             return HttpRequest::REQUEST_OK;  878         }  879         else  880         {  881             return HttpRequest::REQUEST_INVALID_OPT;  882         }  883     }  884   885     return HttpRequest::REQUEST_INIT_ERROR;  886 }  887   888 int HttpDownloader::SetUserData(void* userdata)  889 {  890     if (m_request_handle)  891     {  892         m_request_handle->SetUserData(userdata);  893   894         return HttpRequest::REQUEST_OK;  895     }  896     return HttpRequest::REQUEST_INIT_ERROR;  897 }  898   899 int HttpDownloader::SetRequestId(int id)  900 {  901     if (m_request_handle)  902     {  903         m_request_handle->SetRequestId(id);  904         return HttpRequest::REQUEST_OK;  905     }  906   907     return HttpRequest::REQUEST_INIT_ERROR;  908 }  909   910 int HttpDownloader::SetProgressCallback(ProgressCallback pc)  911 {  912     if (m_request_handle)  913     {  914         m_request_handle->SetProgressCallback(pc);  915   916         return HttpRequest::REQUEST_OK;  917     }  918   919     return HttpRequest::REQUEST_INIT_ERROR;  920 }  921   922 int HttpDownloader::SetResultCallback(ResultCallback rc)  923 {  924     if (m_request_handle)  925     {  926         m_request_handle->SetResultCallback(rc);  927   928         return HttpRequest::REQUEST_OK;  929     }  930   931     return HttpRequest::REQUEST_INIT_ERROR;  932 }  933   934 int HttpDownloader::DownloadFile(const std::string& file_name, int thread_count /* = 5 */)  935 {  936     if (m_request_handle)  937     {  938         m_request_handle->SetDownloadFile(file_name);  939         m_request_handle->SetDownloadThreadCount(thread_count);  940     }  941   942     return HttpRequest::REQUEST_INIT_ERROR;  943 }  944   945 HANDLE HttpDownloader::StartDownload(DownType down_type)  946 {  947     if (m_request_handle)  948     {  949         if (m_request_handle->m_is_running)  950         {  951             return nullptr;  952         }  953   954         if (down_type == DOWN_SYNC)  955         {  956             m_request_handle->Perform();  957   958             return &m_request_handle;  959         }  960         else if (down_type == DOWN_ASYNC)  961         {  962             HttpHelper::s_download_lock.Lock();  963             HttpHelper::s_async_downloads.push_back(m_request_handle);  964             std::shared_ptr<DownloadHelper>& request = HttpHelper::s_async_downloads.back();  965   966 #ifdef _WIN32  967             DWORD thread_id;  968             HANDLE async_thread = CreateThread(NULL, 0, HttpHelper::DownloadThread, &request, 0, &thread_id);  969             request->m_perform_thread = async_thread;  970 #else  971             pthread_create(&(request->m_perform_thread), NULL, HttpHelper::DownloadThread, &request);  972 #endif  973             HttpHelper::s_download_lock.Lock();  974   975             return &request;  976         }  977   978         return nullptr;  979     }  980   981     return nullptr;  982 }  983   984 void HttpDownloader::Close(HANDLE handle)  985 {  986     std::shared_ptr<DownloadHelper>* request = (reinterpret_cast<std::shared_ptr<DownloadHelper> *>(handle));  987     if (request == INVALID_HANDLE_VALUE || request == nullptr)  988     {  989         return;  990     }  991   992     bool basync = false;  993   994     HttpHelper::s_download_lock.Lock();  995     for (auto it = HttpHelper::s_async_downloads.begin(); it != HttpHelper::s_async_downloads.end(); ++it)  996     {  997         if ((*request) == *it)  998         {  999 #ifdef _WIN32 1000             if (WaitForSingleObject((*request)->m_perform_thread, 10) == WAIT_OBJECT_0) 1001 #else 1002             if(pthread_kill((*request)->m_perform_thread, 0) != 0) 1003 #endif 1004             { 1005                 HttpHelper::s_async_downloads.remove(*request); 1006             } 1007             else 1008             { 1009                 (*request)->m_close_self = true; 1010             } 1011             basync = true; 1012             break; 1013         } 1014     } 1015     HttpHelper::s_download_lock.UnLock(); 1016  1017     if (basync == false) 1018     { 1019         (*request)->m_is_cancel = true; 1020         //request->reset(); 1021     } 1022 } 1023  1024 bool HttpDownloader::CancelDownload(HANDLE handle) 1025 { 1026     std::shared_ptr<DownloadHelper>* request = (reinterpret_cast<std::shared_ptr<DownloadHelper> *>(handle)); 1027     if (request == INVALID_HANDLE_VALUE || request == nullptr) 1028     { 1029         return false; 1030     } 1031  1032     (*request)->m_is_cancel = true; 1033  1034     return true; 1035 } 1036  1037 bool HttpDownloader::GetHttpCode(HANDLE handle, long* http_code) 1038 { 1039     std::shared_ptr<DownloadHelper>* request = reinterpret_cast<std::shared_ptr<DownloadHelper>*>(handle); 1040     if (request && http_code) 1041     { 1042         *http_code = (*request)->GetHttpCode(); 1043         return true; 1044     } 1045  1046     return false; 1047 } 1048  1049 bool HttpDownloader::GetErrorString(HANDLE handle, std::string* error_string) 1050 { 1051     std::shared_ptr<DownloadHelper>* request = reinterpret_cast<std::shared_ptr<DownloadHelper>*>(handle); 1052     if (request) 1053     { 1054         return (*request)->GetErrorString(error_string); 1055     } 1056  1057     return false; 1058 } 1059  1060 bool HttpDownloader::GetReceiveHeader(HANDLE handle, std::string* header) 1061 { 1062     std::shared_ptr<DownloadHelper>* request = reinterpret_cast<std::shared_ptr<DownloadHelper>*>(handle); 1063     if (request) 1064     { 1065         return (*request)->GetHeader(header); 1066     } 1067  1068     return false; 1069 } 1070  1071 void* HttpDownloader::GetUserData(HANDLE handle) 1072 { 1073  1074     std::shared_ptr<DownloadHelper>* request = reinterpret_cast<std::shared_ptr<DownloadHelper>*>(handle); 1075     if (request) 1076     { 1077         return (*request)->GetUserData(); 1078     } 1079  1080     return nullptr; 1081 } 1082  1083 HttpDownloader::DownloadHelper::DownloadHelper() 1084 #ifdef _WIN32 1085     : m_perform_thread(nullptr) 1086 #else 1087     : m_perform_thread(-1) 1088 #endif 1089     , m_close_self(false) 1090     , m_retry_times(HttpDownloader::s_kRetryCount) 1091     , m_thread_count(HttpDownloader::s_kThreadCount) 1092     , m_http_code(0) 1093     , m_time_out(0) 1094     , m_proxy_port(0) 1095     , m_total_size(0.0) 1096     , m_downloaded_size(0.0) 1097     , m_multi_download(false) 1098     , m_download_fail(true) 1099     , m_is_running(false) 1100     , m_httplock(new HttpLock) 1101     , m_userdata(NULL) 1102 { 1103     m_download_callback = std::bind(&DownloadHelper::DownloadDefaultCallback, this, 1104         std::placeholders::_1, std::placeholders::_2, std::placeholders::_3); 1105     m_result_callback = std::bind(&DownloadHelper::ResultDefaultCallback, this, 1106         std::placeholders::_1, std::placeholders::_2, std::placeholders::_3); 1107     m_id = HttpHelper::s_id; 1108 } 1109  1110 HttpDownloader::DownloadHelper::~DownloadHelper() 1111 { 1112     if (m_perform_thread) 1113     { 1114 #ifdef _WIN32 1115         CloseHandle(m_perform_thread); 1116         m_perform_thread = nullptr; 1117 #endif 1118     } 1119 } 1120  1121 int HttpDownloader::DownloadHelper::SetTimeout(long time_out /* = 0 */) 1122 { 1123     m_time_out = time_out; 1124  1125     return CURLE_OK; 1126 } 1127  1128 int HttpDownloader::DownloadHelper::SetRequestUrl(const std::string& url) 1129 { 1130     m_url = url; 1131  1132     return CURLE_OK; 1133 } 1134  1135 int HttpDownloader::DownloadHelper::SetRequestProxy(const std::string& proxy, long proxy_port) 1136 { 1137     m_http_proxy = proxy; 1138     m_proxy_port = proxy_port; 1139  1140     return CURLE_OK; 1141 } 1142  1143 int HttpDownloader::DownloadHelper::SetProgressCallback(ProgressCallback pc) 1144 { 1145     m_download_callback = pc; 1146  1147     return CURLE_OK; 1148 } 1149  1150 int HttpDownloader::DownloadHelper::SetResultCallback(ResultCallback rc) 1151 { 1152     m_result_callback = rc; 1153  1154     return CURLE_OK; 1155 } 1156  1157 int HttpDownloader::DownloadHelper::SetDownloadFile(const std::string& file_name) 1158 { 1159     m_file_path = file_name; 1160  1161     return CURLE_OK; 1162 } 1163  1164 int HttpDownloader::DownloadHelper::SetDownloadThreadCount(int thread_count) 1165 { 1166     m_thread_count = thread_count; 1167  1168     return CURLE_OK; 1169 } 1170  1171 int HttpDownloader::DownloadHelper::Perform() 1172 { 1173     m_total_size = GetDownloadFileSize(); 1174     if (m_total_size < 0) 1175     { 1176         return HttpRequest::REQUEST_PERFORM_ERROR; 1177     } 1178  1179     std::string out_file_name = m_file_path; 1180     std::string src_file_name = out_file_name; 1181     out_file_name += ".dl"; 1182  1183     FILE *fp = nullptr; 1184 #ifdef _WIN32 1185     fopen_s(&fp, out_file_name.c_str(), "wb"); 1186 #else 1187     fp = fopen(out_file_name.c_str(), "wb"); 1188 #endif 1189     if (!fp) 1190     { 1191         return HttpRequest::REQUEST_OPENFILE_ERROR; 1192     } 1193  1194     //reset enviroment 1195     m_downloaded_size = 0.0; 1196     m_download_fail = false; 1197     m_is_running = true; 1198     m_is_cancel = false; 1199  1200     int down_code = HttpRequest::REQUEST_PERFORM_ERROR; 1201     int thread_count = SplitDownloadCount(m_total_size); 1202  1203     m_thread_count = thread_count > m_thread_count ? m_thread_count : thread_count; 1204     //文件大小有分开下载的必要并且服务器支持多线程下载时,启用多线程下载 1205     if (m_multi_download && m_thread_count > 1) 1206     { 1207         long gap = static_cast<long>(m_total_size) / m_thread_count; 1208 #ifdef _WIN32 1209         std::vector<HANDLE> threads; 1210 #else 1211         std::vector<pthread_t> threads; 1212 #endif 1213  1214         for (int i = 0; i < m_thread_count; i++) 1215         { 1216             ThreadChunk* thread_chunk = new ThreadChunk; 1217             thread_chunk->_fp = fp; 1218             thread_chunk->_download = this; 1219  1220             if (i < m_thread_count - 1) 1221             { 1222                 thread_chunk->_startidx = i * gap; 1223                 thread_chunk->_endidx = thread_chunk->_startidx + gap - 1; 1224             } 1225             else 1226             { 1227                 thread_chunk->_startidx = i * gap; 1228                 thread_chunk->_endidx = static_cast<long>(m_total_size)-1; 1229             } 1230  1231 #ifdef _WIN32 1232             DWORD thread_id; 1233             HANDLE hThread = CreateThread(NULL, 0, HttpHelper::DownloadWork, thread_chunk, 0, &(thread_id)); 1234 #else 1235             pthread_t hThread; 1236             pthread_create(&hThread, NULL, HttpHelper::DownloadWork, thread_chunk); 1237 #endif 1238             threads.push_back(hThread); 1239         } 1240  1241 #ifdef _WIN32 1242         WaitForMultipleObjects(threads.size(), &threads[0], TRUE, INFINITE); 1243         for (HANDLE handle : threads) 1244         { 1245             CloseHandle(handle); 1246         } 1247 #else 1248         for(pthread_t thread : threads) 1249         { 1250             pthread_join(thread, NULL); 1251         } 1252 #endif 1253     } 1254     else 1255     { 1256         ThreadChunk* thread_chunk = new ThreadChunk; 1257         thread_chunk->_fp = fp; 1258         thread_chunk->_download = this; 1259         thread_chunk->_startidx = 0; 1260         thread_chunk->_endidx = static_cast<long>(m_total_size)-1; 1261         down_code = DoDownload(thread_chunk); 1262     } 1263  1264     if (m_download_fail == false) 1265     { 1266         fclose(fp); 1267 #ifdef _WIN32 1268         MoveFileExA(out_file_name.c_str(), src_file_name.c_str(), MOVEFILE_REPLACE_EXISTING); 1269 #else 1270         unlink(src_file_name.c_str()); 1271         rename(out_file_name.c_str(), src_file_name.c_str()); 1272 #endif 1273     } 1274     else 1275     { 1276 #ifdef _WIN32 1277         DeleteFileA(out_file_name.c_str()); 1278 #else 1279         unlink(out_file_name.c_str()); 1280 #endif 1281     } 1282  1283     m_result_callback(m_id, m_download_fail ? false : true, ""); 1284  1285     m_is_running = false; 1286  1287     return down_code; 1288 } 1289  1290 bool HttpDownloader::DownloadHelper::GetHeader(std::string* header) 1291 { 1292     if (m_receive_header.empty()) return false; 1293     else if (header) *header = m_receive_header; 1294  1295     return true; 1296 } 1297  1298 bool HttpDownloader::DownloadHelper::GetErrorString(std::string* error_string) 1299 { 1300     if (m_error_string.empty()) return false; 1301     else if (error_string) *error_string = m_error_string; 1302  1303     return true; 1304 } 1305  1306 int HttpDownloader::DownloadHelper::DownloadDefaultCallback(double total_size, double downloaded_size, void* userdata) 1307 { 1308     return static_cast<int>(downloaded_size * 100 / total_size); 1309 } 1310  1311 void HttpDownloader::DownloadHelper::ResultDefaultCallback(int id, bool success, const std::string& data) 1312 { 1313 } 1314  1315 double HttpDownloader::DownloadHelper::GetDownloadFileSize() 1316 { 1317     if (m_url.empty()) 1318     { 1319         return -1.0; 1320     } 1321     else 1322     { 1323         double down_file_length = -1.0; 1324         CURL *handle = curl_easy_init(); 1325         HttpHelper::set_share_handle(handle); 1326  1327         if (handle) 1328         { 1329             curl_easy_setopt(handle, CURLOPT_URL, m_url.c_str()); 1330             curl_easy_setopt(handle, CURLOPT_HEADER, 1); 1331             curl_easy_setopt(handle, CURLOPT_NOBODY, 1); 1332             curl_easy_setopt(handle, CURLOPT_FOLLOWLOCATION, 1); 1333             curl_easy_setopt(handle, CURLOPT_MAXREDIRS, 5); 1334             curl_easy_setopt(handle, CURLOPT_HEADERFUNCTION, HttpHelper::RetriveHeaderFunction); 1335             curl_easy_setopt(handle, CURLOPT_HEADERDATA, &m_receive_header); 1336             curl_easy_setopt(handle, CURLOPT_RANGE, "2-"); 1337  1338             CURLcode curl_code = curl_easy_perform(handle); 1339  1340             if (curl_code == CURLE_OPERATION_TIMEDOUT) 1341             { 1342                 int retry_count = m_retry_times; 1343                 while (retry_count > 0) 1344                 { 1345                     curl_code = curl_easy_perform(handle); 1346                     if (curl_code != CURLE_OPERATION_TIMEDOUT) break; 1347                     retry_count--; 1348                 } 1349             } 1350  1351             curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &m_http_code); 1352  1353             if (curl_code == CURLE_OK) 1354             { 1355                 curl_easy_getinfo(handle, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &down_file_length); 1356  1357                 //匹配"Content-Range: bytes 2-1449/26620" 则证明支持多线程下载 1358                 std::regex pattern("CONTENT-RANGE//s*://s*//w+//s*(//d+)-(//d*)/(//d+)", std::regex::icase); 1359                 m_multi_download = std::regex_search(m_receive_header, pattern); 1360             } 1361             else 1362             { 1363                const char* err_string = curl_easy_strerror(curl_code); 1364                m_error_string = err_string; 1365             }             1366  1367             curl_easy_cleanup(handle); 1368         } 1369  1370         return down_file_length; 1371     } 1372 } 1373  1374 int HttpDownloader::DownloadHelper::DoDownload(ThreadChunk* thread_chunk) 1375 { 1376     CURL* curl_handle = curl_easy_init(); 1377     HttpHelper::set_share_handle(curl_handle); 1378  1379     if (thread_chunk->_download->m_url.substr(0, 5) == "https") 1380     { 1381         curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 0L); 1382         curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYHOST, 0L); 1383     } 1384  1385     curl_easy_setopt(curl_handle, CURLOPT_URL, thread_chunk->_download->m_url.c_str()); 1386  1387     const char* user_agent = ("Mozilla/5.0 (Windows NT 5.1; rv:5.0) Gecko/20100101 Firefox/5.0"); 1388     curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, user_agent); 1389  1390     curl_easy_setopt(curl_handle, CURLOPT_MAXREDIRS, 5L); 1391     curl_easy_setopt(curl_handle, CURLOPT_FOLLOWLOCATION, 1L); 1392  1393     curl_easy_setopt(curl_handle, CURLOPT_NOSIGNAL, 1L); 1394     curl_easy_setopt(curl_handle, CURLOPT_POST, 0L); 1395  1396     curl_easy_setopt(curl_handle, CURLOPT_CONNECTTIMEOUT_MS, 0L); 1397     curl_easy_setopt(curl_handle, CURLOPT_TIMEOUT, thread_chunk->_download->m_time_out);   //0 means block always 1398  1399     curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, HttpHelper::write_callback); 1400     curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, thread_chunk); 1401  1402     curl_easy_setopt(curl_handle, CURLOPT_NOPROGRESS, 0L); 1403     curl_easy_setopt(curl_handle, CURLOPT_XFERINFOFUNCTION, HttpHelper::progress_callback); 1404     curl_easy_setopt(curl_handle, CURLOPT_XFERINFODATA, thread_chunk); 1405  1406     curl_easy_setopt(curl_handle, CURLOPT_LOW_SPEED_LIMIT, 1L); 1407     curl_easy_setopt(curl_handle, CURLOPT_LOW_SPEED_TIME, 5L); 1408  1409     std::string down_range; 1410     std::ostringstream ostr; 1411     ostr << thread_chunk->_startidx << "-" << thread_chunk->_endidx; 1412     down_range = ostr.str(); 1413     curl_easy_setopt(curl_handle, CURLOPT_RANGE, down_range.c_str()); 1414  1415     CURLcode curl_code = curl_easy_perform(curl_handle); 1416     if (curl_code == CURLE_OPERATION_TIMEDOUT) 1417     { 1418         int retry_count = m_retry_times; 1419         while (retry_count > 0) 1420         { 1421             curl_code = curl_easy_perform(curl_handle); 1422             if (curl_code != CURLE_OPERATION_TIMEDOUT) break; 1423             retry_count--; 1424         } 1425     } 1426  1427     long http_code; 1428     curl_easy_getinfo(curl_handle, CURLINFO_RESPONSE_CODE, &http_code); 1429     if (curl_code == CURLE_OK && (http_code >= 200 && http_code <= 300)) 1430     { 1431         m_http_code = http_code; 1432     } 1433     else 1434     { 1435         const char* err_string = curl_easy_strerror(curl_code); 1436         m_error_string = err_string; 1437         thread_chunk->_download->m_download_fail = true; 1438         m_http_code = http_code; 1439     } 1440  1441     curl_easy_cleanup(curl_handle); 1442  1443     delete thread_chunk; 1444  1445     return curl_code; 1446 } 1447  1448 int HttpDownloader::DownloadHelper::SplitDownloadCount(double down_size) 1449 { 1450     const double size_2mb = 2.0 * 1024 * 1024; 1451     const double size_10mb = 10.0 * 1024 * 1024; 1452     const double size_50mb = 50.0 * 1024 * 1024; 1453  1454     if (down_size <= size_2mb) 1455     { 1456         return 1; 1457     } 1458     else if (down_size > size_2mb && down_size <= size_10mb) 1459     { 1460         return static_cast<int>(down_size / (size_2mb)); 1461     } 1462     else if (down_size > size_10mb && down_size <= size_50mb) 1463     { 1464         return HttpDownloader::s_kThreadCount + 1; 1465     } 1466     else 1467     { 1468         int down_count = static_cast<int>(down_size / size_10mb); 1469         return down_count > 10 ? 10 : down_count; 1470     } 1471  1472     return 1; 1473 }
    HttpRequest.cpp
  3. 使用libcurl库
    demo使用封装的库来模拟请求数据和下载文件。
    例子很简单,直接看代码:
      1 // http_request.cpp : 定义控制台应用程序的入口点。   2 //   3    4 #include "HttpRequest.h"   5    6 #include <iostream>   7 #include <string>   8 #include <fstream>   9 #include <functional>  10   11 class DownCallbackClass  12 {  13 public:  14         DownCallbackClass() :m_down_finished(false) {}  15         ~DownCallbackClass() {}  16 public:  17         void DownResultCallback(int id, bool success, const std::string& data)  18         {  19                 m_down_finished = true;  20         }  21         int down_callback(double total_size, double downloaded_size, void* userdata)  22         {  23                 long tmp = static_cast<long>(downloaded_size / total_size * 100);  24                 printf("/r下载进度%d", tmp);  25                 return 0;  26         }  27         bool IsDownFinished(void) { return m_down_finished;  }  28 private:  29         bool m_down_finished;  30 };  31   32 class MyResultClass  33 {  34 public:  35         MyResultClass() : m_request_finished(false) { }  36         ~MyResultClass() { }  37   38 public:  39         void MyRequestResultCallback(int id, bool success, const std::string& data)  40         {  41                 if (success)  42                 {  43                         std::ofstream outfile;  44                         outfile.open("baidu.html", std::ios_base::binary | std::ios_base::trunc);  45                         if (outfile.good()) outfile.write(data.c_str(), data.size());  46                 }  47                 m_request_finished = true;  48         }  49         bool IsRequestFinish(void) { return m_request_finished;  }  50 private:  51         bool m_request_finished;  52 };  53   54 int _tmain(int argc, _TCHAR* argv[])  55 {  56         MyResultClass mc;  57   58         HttpRequest request;  59         request.SetRequestUrl("http://www.baidu.com");  60         request.SetResultCallback(std::bind(&MyResultClass::MyRequestResultCallback, &mc, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));  61         request.SetRequestHeader("User-Agent:Mozilla/4.04[en](Win95;I;Nav)");  62   63         HANDLE hRequest = request.PerformRequest(HttpRequest::REQUEST_ASYNC);  64         if (hRequest)  65         {  66                 while (mc.IsRequestFinish() == false) Sleep(300);  67                 long http_code;  68                 if (request.GetHttpCode(hRequest, &http_code))  69                         std::cout << "http code: " << http_code << std::endl;  70   71                 std::string header;  72                 if (request.GetReceiveHeader(hRequest, &header))  73                 {  74                         std::cout << header << std::endl;  75                 }  76   77                 HttpRequest::Close(hRequest);  78         }  79   80         HttpDownloader download;  81         DownCallbackClass dc;  82         const char* down_url = "http://dlsw.baidu.com/sw-search-sp/soft/71/10998/OfflineBaiduPlayer_151_V4.1.2.263.1432003947.exe";  83         const char* down_file = "BaiduPlayer.exe";  84   85         download.SetDownloadUrl(down_url);  86         download.SetProgressCallback(std::bind(&DownCallbackClass::down_callback, &dc, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));  87         download.SetResultCallback(std::bind(&DownCallbackClass::DownResultCallback, &dc, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));  88         download.DownloadFile(down_file);  89         HANDLE hDownload = download.StartDownload(HttpDownloader::DOWN_ASYNC);  90         if (hDownload)  91         {  92                 while (dc.IsDownFinished() == false)  93                 {  94                         Sleep(300);  95                 }  96                 //to do download finish clean up  97                 HttpDownloader::Close(hDownload);  98         }  99  100         return 0; 101 }
正文到此结束
Loading...