• 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(const 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     static bool GetHttpCode(HANDLE request_handle, long* http_code);
      125     static bool GetReceiveHeader(HANDLE request_handle, std::string* header);
      126     static bool GetReceiveContent(HANDLE request_handle, std::string* receive);
      127     static 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("
      下载进度%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     static bool        GetHttpCode(HANDLE handle, long* http_code);
      263     static bool        GetReceiveHeader(HANDLE handle, std::string* header);
      264     static bool        GetErrorString(HANDLE handle, std::string* error_string);
      265     static 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         void  Reset(void);
      314 
      315     private:
      316 #ifdef _WIN32
      317         HANDLE        m_perform_thread;
      318 #else
      319         pthread_t     m_perform_thread;
      320 #endif
      321 
      322         int          m_retry_times;
      323         int          m_thread_count;
      324         int          m_id;
      325         long         m_time_out;
      326 
      327         std::string  m_file_path;
      328         std::string  m_url;
      329         std::string  m_http_proxy;
      330         std::string  m_receive_header;
      331         std::string  m_error_string;
      332 
      333         bool          m_close_self;
      334         bool            m_multi_download;
      335         bool         m_download_fail;
      336         bool          m_is_running;
      337         bool         m_is_cancel;
      338         void*        m_userdata;
      339         long         m_http_code;
      340         long         m_proxy_port;
      341         double       m_total_size;
      342         double       m_downloaded_size;
      343 
      344         std::shared_ptr<HttpLock> m_httplock;
      345         ProgressCallback  m_download_callback;
      346         ResultCallback    m_result_callback;
      347     };
      348 
      349 private:
      350     std::shared_ptr<DownloadHelper>    m_request_handle;
      351 
      352     static const int          s_kRetryCount = 3;
      353     static const int          s_kThreadCount = 4;
      354 };
      355 
      356 #endif  /*__HTTP_REQUEST_H*/
      HttpRequest.h

      实现文件:

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

      libcurl的http请求默认是Get。如果指定了Post数据,则是Post请求。

    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("
      下载进度%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 }
  • 相关阅读:
    html_dom类读取
    PHPExcel读取Excel文件的实现代码
    PHP中的mb_convert_encoding与iconv函数介绍
    BZOJ 3160 万径人踪灭 解题报告
    BZOJ 4036 [HAOI2015] Set 解题报告
    BZOJ 3288 Mato矩阵 解题报告
    BZOJ 3173 [Tjoi2013] 最长上升子序列 解题报告
    BZOJ 4123 [Baltic2015] Hacker 解题报告
    BZOJ 4127 Abs 解题报告
    BZOJ 4145 [AMPPZ2014] The Prices 解题报告
  • 原文地址:https://www.cnblogs.com/jojodru/p/4551201.html
Copyright © 2020-2023  润新知