libcurl的封装,支持同步异步请求,支持多线程下载,支持https(z)
生活随笔
收集整理的這篇文章主要介紹了
libcurl的封装,支持同步异步请求,支持多线程下载,支持https(z)
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
最近在做一個項目,需要用到http get post等
需求分析需要做到同步和異步,異步請求的返回以可選的回調(diào)通知的方式進(jìn)行。
本人以Linux為例,一步一步的來實現(xiàn)。
我以在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/bash2 # Cross-compile environment for Android on ARMv7 and x863 #4 # Contents licensed under the terms of the OpenSSL license5 # http://www.openssl.org/source/license.html6 #7 # See http://wiki.openssl.org/index.php/FIPS_Library_and_Android8 # and http://wiki.openssl.org/index.php/Android9 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 a14 # login script. If ANDROID_NDK_ROOT is not specified, the script will15 # try to pick it up with the value of _ANDROID_NDK_ROOT below. If16 # 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-r10d21 # Set _ANDROID_EABI to the EABI you want to use. You can find the22 # 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-x8631 _ANDROID_ARCH=arch-arm32 33 # Set _ANDROID_API to the API you want to use. You should set it34 # to one of: android-14, android-9, android-8, android-14, android-535 # android-4, or android-3. You can't set it to the latest (for36 # example, API-17) because the NDK does not supply the platform. At37 # 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-r8e48 # or ANDROID_NDK_ROOT=/usr/local/android-ndk-r8e.49 50 if [ -z "$ANDROID_NDK_ROOT" ]; then51 52 _ANDROID_NDK_ROOT=""53 if [ -z "$_ANDROID_NDK_ROOT" ] && [ -d "/usr/local/$_ANDROID_NDK" ]; then54 _ANDROID_NDK_ROOT="/usr/local/$_ANDROID_NDK"55 fi56 57 if [ -z "$_ANDROID_NDK_ROOT" ] && [ -d "/opt/$_ANDROID_NDK" ]; then58 _ANDROID_NDK_ROOT="/opt/$_ANDROID_NDK"59 fi60 61 if [ -z "$_ANDROID_NDK_ROOT" ] && [ -d "$HOME/$_ANDROID_NDK" ]; then62 _ANDROID_NDK_ROOT="$HOME/$_ANDROID_NDK"63 fi64 65 if [ -z "$_ANDROID_NDK_ROOT" ] && [ -d "$PWD/$_ANDROID_NDK" ]; then66 _ANDROID_NDK_ROOT="$PWD/$_ANDROID_NDK"67 fi68 69 # If a path was set, then export it70 if [ ! -z "$_ANDROID_NDK_ROOT" ] && [ -d "$_ANDROID_NDK_ROOT" ]; then71 export ANDROID_NDK_ROOT="$_ANDROID_NDK_ROOT"72 fi73 fi74 75 # Error checking76 # 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/a998e139aca71d7778 if [ -z "$ANDROID_NDK_ROOT" ] || [ ! -d "$ANDROID_NDK_ROOT" ]; then79 echo "Error: ANDROID_NDK_ROOT is not a valid path. Please edit this script."80 # echo "$ANDROID_NDK_ROOT"81 # exit 182 fi83 84 # Error checking85 if [ ! -d "$ANDROID_NDK_ROOT/toolchains" ]; then86 echo "Error: ANDROID_NDK_ROOT/toolchains is not a valid path. Please edit this script."87 # echo "$ANDROID_NDK_ROOT/toolchains"88 # exit 189 fi90 91 # Error checking92 if [ ! -d "$ANDROID_NDK_ROOT/toolchains/$_ANDROID_EABI" ]; then93 echo "Error: ANDROID_EABI is not a valid path. Please edit this script."94 # echo "$ANDROID_NDK_ROOT/toolchains/$_ANDROID_EABI"95 # exit 196 fi97 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并且編譯
配置腳本:
配置成功之后,cd進(jìn)代碼目錄執(zhí)行make && make install命令即可
1.3 配置libcurl并且編譯
1 #!/bin/sh2 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進(jìn)代碼目錄執(zhí)行make && make install命令即可
本配置使用的是android的ndk工具鏈gcc 4.8在配置openssl時,指定了ANDROID_NDK_ROOT的值為ndk的路徑,可以參看腳本的值進(jìn)行對應(yīng)的設(shè)置
可以在ndk目錄的build/tools目錄找到make-standalone-toolchain.sh文件,執(zhí)行make-standalone-toolchain.sh --help --help來查看幫助
構(gòu)建自己的ndk gcc工具鏈,最后將生成的工具鏈路徑加入進(jìn)環(huán)境變量PATH即可
代碼使用C++封裝,并且使用了C++11的特性,編譯時需要指定-std=c++11
頭文件: 1 #ifndef __HTTP_REQUEST_H2 #define __HTTP_REQUEST_H3 4 5 #include <string>6 #include <map>7 #include <memory>8 #include <functional>9 #include <vector>10 11 //************************************12 // Usage: 13 // class MyResultClass14 // {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 _WIN3258 typedef void* HANDLE;59 #endif60 61 class HttpRequest62 {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& data78 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: SetMovedUrl93 // FullName: HttpRequest::SetMovedUrl94 // Access: public 95 // Returns: int96 // Description: set http redirect follow location97 // Parameter: bool get_moved_url -- true means redirect http url98 //************************************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("\r下載進(jìn)度%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*/
實現(xiàn)文件:
1 // [5/11/2015 Carbon]2 /*3 _ooOoo_4 o888888888o 5 888 " . " 8886 (| -_- |) 7 O\ = /O 8 ____/` --- '\____ 9 .' \\| |// `. 10 / \\||| : |||// \ 11 / _||||| -:- |||||- \ 12 | | \\\ - /// | | 13 | \_| ''\---/'' |_/ | 14 \ .-\__ `-` __/-. / 15 _____`. .' /--.--\ `. . _____ 16 ."" '< `.___\_ <|> _/___.' >' "". 17 | | : `- \`.;` \ _ / `;.`/ - ` : | | 18 \ \ `-. \_ __\ /__ _/ .-` / / 19 ========`-.____`-.___\_____/___.-`____.-'======== 20 `=---='21 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^22 佛祖保佑 永無BUG23 */ 24 #ifdef _WIN3225 #include "stdafx.h"26 #else27 #include <pthread.h>28 #include <stdio.h>29 #include <unistd.h>30 #endif31 32 #include "./curl/curl.h" //libcurl interface33 #include "HttpRequest.h" //HttpRequest class34 35 #include <list>36 #include <regex>37 #include <sstream>38 39 40 #ifndef _WIN3241 typedef unsigned long DWORD;42 #define INVALID_HANDLE_VALUE (void*)0xffffffff43 #define TRUE 144 #define FALSE 045 #endif //#ifndef _WIN3246 47 class HttpLock48 {49 public:50 #ifdef _WIN3251 HttpLock() { InitializeCriticalSection(&m_cs); }52 ~HttpLock() { DeleteCriticalSection(&m_cs); }53 54 void Lock() { EnterCriticalSection(&m_cs); }55 void UnLock() { LeaveCriticalSection(&m_cs); }56 #else57 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 #endif63 64 private:65 #ifdef _WIN3266 CRITICAL_SECTION m_cs;67 #else68 pthread_mutex_t m_lock;69 #endif70 };71 72 class DoHttpLock73 {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 _WIN32132 static DWORD WINAPI RequestThread(LPVOID param)133 #else134 static void* RequestThread(void* param)135 #endif136 {137 #ifdef _WIN32138 Sleep(10);139 #else140 usleep(10 * 1000);141 #endif142 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 _WIN32157 return 1;158 #else159 return NULL;160 #endif161 }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 _WIN32186 static DWORD WINAPI DownloadThread(LPVOID param)187 #else188 static void* DownloadThread(void* param)189 #endif190 {191 #ifdef _WIN32192 Sleep(10);193 #else194 usleep(10 * 1000);195 #endif196 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 _WIN32212 return 1;213 #else214 return NULL;215 #endif216 }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 else247 {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 _WIN32271 static DWORD WINAPI DownloadWork(LPVOID param)272 #else273 static void* DownloadWork(void* param)274 #endif275 {276 HttpDownloader::DownloadHelper::ThreadChunk* thread_chunk = reinterpret_cast<HttpDownloader::DownloadHelper::ThreadChunk*>(param);277 278 #ifdef _WIN32279 return thread_chunk->_download->DoDownload(thread_chunk);280 #else281 return (void *)(thread_chunk->_download->DoDownload(thread_chunk));282 #endif283 }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 else334 {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 else351 {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 else368 {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 else390 {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 else426 {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 else442 {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 _WIN32477 if (WaitForSingleObject((*request)->m_perform_thread, 10) == WAIT_OBJECT_0)478 #else479 if(pthread_kill((*request)->m_perform_thread, 0) != 0)480 #endif481 {482 HttpHelper::s_async_requests.remove(*request);483 }484 else485 {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 _WIN32522 DWORD thread_id;523 HANDLE async_thread = CreateThread(NULL, 0, HttpHelper::RequestThread, &request, 0, &thread_id);524 request->m_perform_thread = async_thread;525 #else526 pthread_create(&(request->m_perform_thread), NULL, HttpHelper::RequestThread, &request);527 #endif528 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 _WIN32586 , m_perform_thread(nullptr)587 #else588 , m_perform_thread(-1)589 #endif590 , 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 _WIN32619 if (m_perform_thread)620 {621 CloseHandle(m_perform_thread);622 }623 #endif624 }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 else662 {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 else688 {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_PROXY721 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 nothing743 }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 redirect764 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 else795 {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 else860 {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 else888 {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 else905 {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 _WIN32994 DWORD thread_id;995 HANDLE async_thread = CreateThread(NULL, 0, HttpHelper::DownloadThread, &request, 0, &thread_id);996 request->m_perform_thread = async_thread;997 #else998 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 //文件大小有分開下載的必要并且服務(wù)器支持多線程下載時,啟用多線程下載 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請求默認(rèn)是Get。如果指定了Post數(shù)據(jù),則是Post請求。
?
demo使用封裝的庫來模擬請求數(shù)據(jù)和下載文件。
例子很簡單,直接看代碼:
1 // http_request.cpp : 定義控制臺應(yīng)用程序的入口點。2 //3 4 #include "HttpRequest.h"5 6 #include <iostream>7 #include <string>8 #include <fstream>9 #include <functional>10 11 class DownCallbackClass12 {13 public:14 DownCallbackClass() :m_down_finished(false) {}15 ~DownCallbackClass() {}16 public:17 void DownResultCallback(int id, bool success, const std::string& data)18 {19 m_down_finished = true;20 }21 int down_callback(double total_size, double downloaded_size, void* userdata)22 {23 long tmp = static_cast<long>(downloaded_size / total_size * 100);24 printf("\r下載進(jìn)度%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 MyResultClass33 {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 up97 HttpDownloader::Close(hDownload);98 }99 100 return 0; 101 }
?
轉(zhuǎn)載于:https://www.cnblogs.com/lehoho/p/9367287.html
總結(jié)
以上是生活随笔為你收集整理的libcurl的封装,支持同步异步请求,支持多线程下载,支持https(z)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 微信支付V3版开发中遇到的一个问题及原因
- 下一篇: 单例模式( Single Pattern