码迷,mamicode.com
首页 > 编程语言 > 详细

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

时间:2015-06-04 11:21:01      阅读:257      评论:0      收藏:0      [点我收藏+]

标签:

最近在做一个项目,需要用到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 cant 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 (Dont 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 }

     

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

标签:

原文地址:http://www.cnblogs.com/jojodru/p/4551201.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!