libcurl的封装,支持同步异步请求,支持多线程下载,支持https(z)
阅读原文时间:2021年04月21日阅读:1

最近在做一个项目,需要用到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
    6 #include
    7 #include
    8 #include
    9 #include
    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 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&
    111 // Parameter: std::string> & headers
    112 //************************************
    113 int SetRequestHeader(const std::map& 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 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(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 ProgressCallback;
    240 //int id, bool success, const std::string& data
    241 typedef std::function 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 m_httplock;
    345 ProgressCallback m_download_callback;
    346 ResultCallback m_result_callback;
    347 };
    348
    349 private:
    350 std::shared_ptr 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
    28 #include
    29 #include
    30 #endif
    31
    32 #include "./curl/curl.h" //libcurl interface
    33 #include "HttpRequest.h" //HttpRequest class
    34
    35 #include
    36 #include
    37 #include
    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 & 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 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 > s_async_requests;
    124 static std::list< std::shared_ptr > s_async_downloads;
    125
    126 static int s_id;
    127 static std::shared_ptr s_request_lock;
    128 static std::shared_ptr 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* request = reinterpret_cast*>(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(userdata);
    166 if (receive_header && buffer)
    167 {
    168 receive_header->append(reinterpret_cast(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(userdata);
    177 if (receive_content && ptr)
    178 {
    179 receive_content->append(reinterpret_cast(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* request = reinterpret_cast*>(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(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(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(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 > HttpHelper::s_async_requests;
    287 std::list< std::shared_ptr > HttpHelper::s_async_downloads;
    288 int HttpHelper::s_id = 0;
    289 std::shared_ptr HttpHelper::s_request_lock(new HttpLock);
    290 std::shared_ptr 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& 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* request = (reinterpret_cast *>(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& 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* request = reinterpret_cast*>(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* request = reinterpret_cast*>(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* request = reinterpret_cast*>(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* request = reinterpret_cast*>(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(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(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(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(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& 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* request = (reinterpret_cast *>(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* request = (reinterpret_cast *>(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* request = reinterpret_cast*>(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* request = reinterpret_cast*>(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* request = reinterpret_cast*>(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* request = reinterpret_cast*>(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(m_total_size) / m_thread_count;
    1229 #ifdef _WIN32
    1230 std::vector threads;
    1231 #else
    1232 std::vector 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(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(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
    7 #include
    8 #include
    9 #include
    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(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 }

转载于:https://www.cnblogs.com/lehoho/p/9367287.html

手机扫一扫

移动阅读更方便

阿里云服务器
腾讯云服务器
七牛云服务器

你可能感兴趣的文章