zoukankan      html  css  js  c++  java
  • libcurl的封装,支持同步异步请求,支持多线程下载,支持https(z)

    最近在做一个项目,需要用到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
      复制代码

      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
      复制代码

      配置成功之后,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
      复制代码

      配置成功之后,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*/
      复制代码

      实现文件:

      复制代码
         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 }
      复制代码

      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 }
      复制代码
  • 相关阅读:
    使用C#实现DHT磁力搜索的BT种子后端管理程序+数据库设计(开源)
    便携版WinSCP在命令行下同步文件夹
    ffmpeg (ffprobe)分析文件关键帧时间点
    sqlite删除数据或者表后,回收数据库文件大小
    ubuntu 20.04下 freeswitch 配合 fail2ban 防恶意访问
    ffmpeg使用nvenc编码的结论记录
    PC版跑跑卡丁车 故事模式 亚瑟传说章节 卡美洛庆典 2阶段 心灵之眼 攻略
    There was an error loading or playing the video
    Nvidia RTX Voice 启动报错修复方法
    火狐浏览器 关闭跨域限制
  • 原文地址:https://www.cnblogs.com/lehoho/p/9367287.html
Copyright © 2011-2022 走看看