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