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并且编译
    配置和编译脚本:

      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并且编译
    配置脚本:

     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并且编译

    配置脚本:

     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
    头文件:

      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

    实现文件:

       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 }
时间: 2024-08-04 21:41:02

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

Android简单封装类似JQuery异步请求

在android开发中经常会使用异步请求数据,通常会使用handler或者AsyncTask去做,handler 配合message 使用起来比较麻烦,AsyncTask 线程池只允许128个线程工作,会有溢出的问题,(当然一般情况不会有那么多线程同时工作的)所以写了这个代码,还望高手指正! [Java]代码 01 package com.xbl.task; 02 03 import java.io.BufferedReader; 04 import java.io.InputStream; 0

Springmvc中 同步/异步请求参数的传递以及数据的返回

注意: 这里的返回就是返回到jsp页面 **** controller接收前台数据的方式,以及将处理后的model 传向前台***** 1.前台传递数据的接受:传的属性名和javabean的属性相同 (1).使用基本类型,或引用类型进行接受: @RequestMapping(value="/select") PublicString  select(String name,int age,Model model){ // 这样这里的name,age 就是我们前台传递的参数,也是我们Ja

AFN同步异步请求

异步请求: -(BOOL)getOnlyKey1 { NSString *myUUIDStr = [[[UIDevice currentDevice] identifierForVendor] UUIDString]; __block bool isTrue = false; AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager]; manager.responseSerializer.ac

NSURLConnection同步异步请求

以百度图片为例: 先在xib文件中添加一个UIImageView控件 一:同步方法 1 //获取URL 2 NSURL *url=[NSURL URLWithString:@"http://pic1a.nipic.com/2008-11-13/200811133748109_2.jpg"]; 3 //发送请求 4 NSURLRequest *request=[NSURLRequest requestWithURL:url]; 5 //发起同步请求 6 NSData *data=[NSU

Ajax同步异步请求

一.什么是同步请求:(false)       同步请求即是当前发出请求后,浏览器什么都不能做,必须得等到请求完成返回数据之后,才会执行后续的代码,相当于是排队,前一个人办理完自己的事务,下一个人才能接着办.也就是说,当JS代码加载到当前AJAX的时候会把页面里所有的代码停止加载,页面处于一个假死状态,当这个AJAX执行完毕后才会继续运行其他代码页面解除假死状态. 二.什么是异步请求:(true)       异步请求就当发出请求的同时,浏览器可以继续做任何事,Ajax发送请求并不会影响页面的加

从零开始学 Web 之 Ajax(五)同步异步请求,数据格式

大家好,这里是「 从零开始学 Web 系列教程 」,并在下列地址同步更新...... github:https://github.com/Daotin/Web 微信公众号:Web前端之巅 博客园:http://www.cnblogs.com/lvonve/ CSDN:https://blog.csdn.net/lvonve/ 在这里我会从 Web 前端零基础开始,一步步学习 Web 相关的知识点,期间也会分享一些好玩的项目.现在就让我们一起进入 Web 前端学习的冒险之旅吧! 一.同步请求与异步

jQuery基础(Ajax,load(),getJSON(),getScript(),post(),ajax(),同步/异步请求数据)

1.使用load()方法异步请求数据 使用load()方法通过Ajax请求加载服务器中的数据,并把返回的数据放置到指定的元素中,它的调用格式为: load(url,[data],[callback]) 参数url为加载服务器地址,可选项data参数为请求时发送的数据,callback参数为数据请求成功后,执行的回调函数. 2.使用getJSON()方法异步加载JSON格式数据 使用getJSON()方法可以通过Ajax异步请求的方式,获取服务器中的数据,并对获取的数据进行解析,显示在页面中,它的

简单的 同步 异步 请求

#import "ViewController.h" @interface ViewController () @property(nonatomic,strong)UITextView *textView; @property(nonatomic,copy)NSString *BASE_URL; @property(nonatomic,copy)NSString *BASE_URL1_PARAM; @property(nonatomic,strong)NSMutableData *m

IOS中get同步异步请求与post同步异步请求

demo //  Created by apple on 15/1/6. //  Copyright (c) 2015年 huweibin. All rights reserved. // #import "ViewController.h" @interface ViewController () @property(nonatomic,strong)UITextView *textView; @property(nonatomic,copy)NSString *BASE_URL;