日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

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

發布時間:2023/12/14 编程问答 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 libcurl的封装,支持同步异步请求,支持多线程下载,支持https(z) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

最近在做一個項目,需要用到http get post等

需求分析需要做到同步和異步,異步請求的返回以可選的回調通知的方式進行。

本人以Linux為例,一步一步的來實現。

  • 配置并且編譯libcurl
    我以在Linux底下的交叉編譯舉例。
    libcurl源碼下載:?http://curl.haxx.se/download.html
    配置libcurl支持https和zlib壓縮,必須需要openssl和zlib庫
    openssl庫源碼下載:?http://www.openssl.org/source/。下載1.02a以上的版本,避開心臟出血漏洞。
    zlib源碼下載:http://www.zlib.net/。下載最新版本代碼。
    新建文件夾carbon。源碼解壓至目錄carbon。

    1.1 配置openssl并且編譯
    配置和編譯腳本:
    1 #!/bin/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并且編譯
    配置腳本:

    1 #!/bin/sh2 3 export ROOTDIR="${PWD}"4 cd zlib/5 6 export CROSS_COMPILE="arm-linux-androideabi"7 export CPPFLAGS="-fPIC"8 export CFLAGS="-fPIC"9 export AR=${CROSS_COMPILE}-ar 10 export AS=${CROSS_COMPILE}-as 11 export LD=${CROSS_COMPILE}-ld 12 export RANLIB=${CROSS_COMPILE}-ranlib 13 export CC=${CROSS_COMPILE}-gcc 14 export CXX=${CROSS_COMPILE}-g++ 15 export NM=${CROSS_COMPILE}-nm 16 17 ./configure --prefix=${ROOTDIR}/build/zlib --static

    配置成功之后,cd進代碼目錄執行make && make install命令即可

    1.3 配置libcurl并且編譯

    配置腳本:
    1 #!/bin/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進代碼目錄執行make && make install命令即可

    本配置使用的是android的ndk工具鏈gcc 4.8
    在配置openssl時,指定了ANDROID_NDK_ROOT的值為ndk的路徑,可以參看腳本的值進行對應的設置
    可以在ndk目錄的build/tools目錄找到make-standalone-toolchain.sh文件,執行make-standalone-toolchain.sh --help --help來查看幫助
    構建自己的ndk gcc工具鏈,最后將生成的工具鏈路徑加入進環境變量PATH即可

  • 封裝libcurl庫
    代碼使用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下載進度%d", tmp); 206 // return 0; 207 // } 208 // bool IsDownFinished(void) { return m_down_finished; } 209 // private: 210 // bool m_down_finished; 211 // }; 212 // HttpDownloader download; 213 // DownCallbackClass dc; 214 // const char* down_url = "http://dlsw.baidu.com/sw-search-sp/soft/71/10998/OfflineBaiduPlayer_151_V4.1.2.263.1432003947.exe"; 215 // const char* down_file = "BaiduPlayer.exe"; 216 // 217 // download.SetDownloadUrl(down_url); 218 // download.SetProgressCallback(std::bind(&DownCallbackClass::down_callback, &dc, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); 219 // download.SetResultCallback(std::bind(&DownCallbackClass::DownResultCallback, &dc, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); 220 // download.DownloadFile(down_file); 221 // HANDLE hDownload = download.StartDownload(HttpDownloader::DOWN_ASYNC); 222 // if (hDownload) 223 // { 224 // while (dc.IsDownFinished() == false) Sleep(300); 225 // //to do download finish clean up 226 // HttpDownloader::Close(hDownload); 227 // } 228 //************************************ 229 230 class HttpDownloader 231 { 232 public: 233 typedef enum { 234 DOWN_SYNC, 235 DOWN_ASYNC, 236 }DownType; 237 238 //double total_size, double downloaded_size, void* userdata 239 typedef std::function<int(double, double, void*)> ProgressCallback; 240 //int id, bool success, const std::string& data 241 typedef std::function<void(int, bool, const std::string&)> ResultCallback; 242 243 friend class HttpHelper; 244 245 HttpDownloader(); 246 ~HttpDownloader(); 247 248 int SetRequestProxy(const std::string& proxy, long proxy_port); 249 int SetRetryTimes(int retry_times = s_kRetryCount); 250 int SetTimeout(long time_out = 0); 251 int SetDownloadUrl(const std::string& url); 252 int SetUserData(void* userdata); 253 int SetRequestId(int id); 254 int SetProgressCallback(ProgressCallback pc); 255 int SetResultCallback(ResultCallback rc); 256 257 int DownloadFile(const std::string& file_name, int thread_count = 5); 258 HANDLE StartDownload(DownType down_type); 259 static bool CancelDownload(HANDLE handle); 260 static void Close(HANDLE handle); 261 262 static bool GetHttpCode(HANDLE handle, long* http_code); 263 static bool GetReceiveHeader(HANDLE handle, std::string* header); 264 static bool GetErrorString(HANDLE handle, std::string* error_string); 265 static void* GetUserData(HANDLE handle); 266 267 protected: 268 269 class DownloadHelper { 270 public: 271 typedef struct tThreadChunk 272 { 273 FILE* _fp; 274 long _startidx; 275 long _endidx; 276 277 DownloadHelper* _download; 278 }ThreadChunk; 279 280 DownloadHelper(); 281 ~DownloadHelper(); 282 283 friend class HttpDownloader; 284 friend class HttpHelper; 285 friend ThreadChunk; 286 287 void SetRetryTimes(int retry_times) { m_retry_times = retry_times; } 288 void SetRequestId(int id) { m_id = id; } 289 int SetTimeout(long time_out = 0); 290 int SetRequestUrl(const std::string& url); 291 int SetRequestProxy(const std::string& proxy, long proxy_port); 292 293 void SetUserData(void *userdata) { m_userdata = userdata; } 294 int SetProgressCallback(ProgressCallback pc); 295 int SetResultCallback(ResultCallback rc); 296 int SetDownloadFile(const std::string& file_name); 297 int SetDownloadThreadCount(int thread_count); 298 299 int Perform(); 300 301 int GetHttpCode() { return m_http_code; } 302 bool GetHeader(std::string* header); 303 bool GetErrorString(std::string* error_string); 304 bool SelfClose(void) { return m_close_self; } 305 void* GetUserData(void) { return m_userdata; } 306 307 protected: 308 int DownloadDefaultCallback(double total_size, double downloaded_size, void* userdata); 309 void ResultDefaultCallback(int id, bool success, const std::string& data); 310 double GetDownloadFileSize(); 311 int DoDownload(ThreadChunk* thread_chunk); 312 int SplitDownloadCount(double down_size); 313 void Reset(void); 314 315 private: 316 #ifdef _WIN32 317 HANDLE m_perform_thread; 318 #else 319 pthread_t m_perform_thread; 320 #endif 321 322 int m_retry_times; 323 int m_thread_count; 324 int m_id; 325 long m_time_out; 326 327 std::string m_file_path; 328 std::string m_url; 329 std::string m_http_proxy; 330 std::string m_receive_header; 331 std::string m_error_string; 332 333 bool m_close_self; 334 bool m_multi_download; 335 bool m_download_fail; 336 bool m_is_running; 337 bool m_is_cancel; 338 void* m_userdata; 339 long m_http_code; 340 long m_proxy_port; 341 double m_total_size; 342 double m_downloaded_size; 343 344 std::shared_ptr<HttpLock> m_httplock; 345 ProgressCallback m_download_callback; 346 ResultCallback m_result_callback; 347 }; 348 349 private: 350 std::shared_ptr<DownloadHelper> m_request_handle; 351 352 static const int s_kRetryCount = 3; 353 static const int s_kThreadCount = 4; 354 }; 355 356 #endif /*__HTTP_REQUEST_H*/

    實現文件:

    1 // [5/11/2015 Carbon]2 /*3 _ooOoo_4 o888888888o 5 888 " . " 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 //文件大小有分開下載的必要并且服務器支持多線程下載時,啟用多線程下載 1226 if (m_multi_download && m_thread_count > 1) 1227 { 1228 long gap = static_cast<long>(m_total_size) / m_thread_count; 1229 #ifdef _WIN32 1230 std::vector<HANDLE> threads; 1231 #else 1232 std::vector<pthread_t> threads; 1233 #endif 1234 1235 for (int i = 0; i < m_thread_count; i++) 1236 { 1237 ThreadChunk* thread_chunk = new ThreadChunk; 1238 thread_chunk->_fp = fp; 1239 thread_chunk->_download = this; 1240 1241 if (i < m_thread_count - 1) 1242 { 1243 thread_chunk->_startidx = i * gap; 1244 thread_chunk->_endidx = thread_chunk->_startidx + gap - 1; 1245 } 1246 else 1247 { 1248 thread_chunk->_startidx = i * gap; 1249 thread_chunk->_endidx = -1; 1250 } 1251 1252 #ifdef _WIN32 1253 DWORD thread_id; 1254 HANDLE hThread = CreateThread(NULL, 0, HttpHelper::DownloadWork, thread_chunk, 0, &(thread_id)); 1255 #else 1256 pthread_t hThread; 1257 pthread_create(&hThread, NULL, HttpHelper::DownloadWork, thread_chunk); 1258 #endif 1259 threads.push_back(hThread); 1260 } 1261 1262 #ifdef _WIN32 1263 WaitForMultipleObjects(threads.size(), &threads[0], TRUE, INFINITE); 1264 for (HANDLE handle : threads) 1265 { 1266 CloseHandle(handle); 1267 } 1268 #else 1269 for(pthread_t thread : threads) 1270 { 1271 pthread_join(thread, NULL); 1272 } 1273 #endif 1274 } 1275 else 1276 { 1277 ThreadChunk* thread_chunk = new ThreadChunk; 1278 thread_chunk->_fp = fp; 1279 thread_chunk->_download = this; 1280 thread_chunk->_startidx = 0; 1281 thread_chunk->_endidx = 0; 1282 down_code = DoDownload(thread_chunk); 1283 } 1284 1285 fclose(fp); 1286 1287 if (m_download_fail == false) 1288 { 1289 #ifdef _WIN32 1290 MoveFileExA(out_file_name.c_str(), src_file_name.c_str(), MOVEFILE_REPLACE_EXISTING); 1291 #else 1292 unlink(src_file_name.c_str()); 1293 rename(out_file_name.c_str(), src_file_name.c_str()); 1294 #endif 1295 } 1296 else 1297 { 1298 #ifdef _WIN32 1299 DeleteFileA(out_file_name.c_str()); 1300 #else 1301 unlink(out_file_name.c_str()); 1302 #endif 1303 } 1304 1305 m_result_callback(m_id, m_download_fail ? false : true, m_error_string); 1306 1307 m_is_running = false; 1308 1309 return down_code; 1310 } 1311 1312 bool HttpDownloader::DownloadHelper::GetHeader(std::string* header) 1313 { 1314 if (m_receive_header.empty()) return false; 1315 else if (header) *header = m_receive_header; 1316 1317 return true; 1318 } 1319 1320 bool HttpDownloader::DownloadHelper::GetErrorString(std::string* error_string) 1321 { 1322 if (m_error_string.empty()) return false; 1323 else if (error_string) *error_string = m_error_string; 1324 1325 return true; 1326 } 1327 1328 int HttpDownloader::DownloadHelper::DownloadDefaultCallback(double total_size, double downloaded_size, void* userdata) 1329 { 1330 return 0; 1331 } 1332 1333 void HttpDownloader::DownloadHelper::ResultDefaultCallback(int id, bool success, const std::string& data) 1334 { 1335 } 1336 1337 double HttpDownloader::DownloadHelper::GetDownloadFileSize() 1338 { 1339 if (m_url.empty()) 1340 { 1341 return -1.0; 1342 } 1343 else 1344 { 1345 double down_file_length = -1.0; 1346 CURL *handle = curl_easy_init(); 1347 HttpHelper::set_share_handle(handle); 1348 1349 if (handle) 1350 { 1351 curl_easy_setopt(handle, CURLOPT_URL, m_url.c_str()); 1352 curl_easy_setopt(handle, CURLOPT_HEADER, 1); 1353 curl_easy_setopt(handle, CURLOPT_NOBODY, 1); 1354 curl_easy_setopt(handle, CURLOPT_FOLLOWLOCATION, 1); 1355 curl_easy_setopt(handle, CURLOPT_MAXREDIRS, 5); 1356 curl_easy_setopt(handle, CURLOPT_HEADERFUNCTION, HttpHelper::RetriveHeaderFunction); 1357 curl_easy_setopt(handle, CURLOPT_HEADERDATA, &m_receive_header); 1358 curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, HttpHelper::RetriveContentFunction); 1359 curl_easy_setopt(handle, CURLOPT_WRITEDATA, NULL); 1360 curl_easy_setopt(handle, CURLOPT_RANGE, "2-"); 1361 1362 CURLcode curl_code = curl_easy_perform(handle); 1363 1364 if (curl_code == CURLE_OPERATION_TIMEDOUT) 1365 { 1366 int retry_count = m_retry_times; 1367 while (retry_count > 0) 1368 { 1369 curl_code = curl_easy_perform(handle); 1370 if (curl_code != CURLE_OPERATION_TIMEDOUT) break; 1371 retry_count--; 1372 } 1373 } 1374 1375 curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &m_http_code); 1376 1377 if (curl_code == CURLE_OK) 1378 { 1379 curl_easy_getinfo(handle, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &down_file_length); 1380 1381 //匹配"Content-Range: bytes 2-1449/26620" 則證明支持多線程下載 1382 std::regex pattern("CONTENT-RANGE\\s*:\\s*\\w+\\s*(\\d+)-(\\d*)/(\\d+)", std::regex::icase); 1383 m_multi_download = std::regex_search(m_receive_header, pattern); 1384 } 1385 else 1386 { 1387 const char* err_string = curl_easy_strerror(curl_code); 1388 m_error_string = err_string; 1389 } 1390 1391 curl_easy_cleanup(handle); 1392 } 1393 1394 return down_file_length; 1395 } 1396 } 1397 1398 int HttpDownloader::DownloadHelper::DoDownload(ThreadChunk* thread_chunk) 1399 { 1400 CURL* curl_handle = curl_easy_init(); 1401 HttpHelper::set_share_handle(curl_handle); 1402 1403 if (thread_chunk->_download->m_url.substr(0, 5) == "https") 1404 { 1405 curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 0L); 1406 curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYHOST, 0L); 1407 } 1408 1409 curl_easy_setopt(curl_handle, CURLOPT_URL, thread_chunk->_download->m_url.c_str()); 1410 1411 const char* user_agent = ("Mozilla/5.0 (Windows NT 5.1; rv:5.0) Gecko/20100101 Firefox/5.0"); 1412 curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, user_agent); 1413 1414 curl_easy_setopt(curl_handle, CURLOPT_MAXREDIRS, 5L); 1415 curl_easy_setopt(curl_handle, CURLOPT_FOLLOWLOCATION, 1L); 1416 1417 curl_easy_setopt(curl_handle, CURLOPT_NOSIGNAL, 1L); 1418 curl_easy_setopt(curl_handle, CURLOPT_POST, 0L); 1419 1420 curl_easy_setopt(curl_handle, CURLOPT_CONNECTTIMEOUT_MS, 0L); 1421 curl_easy_setopt(curl_handle, CURLOPT_TIMEOUT, thread_chunk->_download->m_time_out); //0 means block always 1422 1423 curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, HttpHelper::write_callback); 1424 curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, thread_chunk); 1425 curl_easy_setopt(curl_handle, CURLOPT_HEADERFUNCTION, HttpHelper::RetriveHeaderFunction); 1426 curl_easy_setopt(curl_handle, CURLOPT_HEADERDATA, NULL); 1427 1428 curl_easy_setopt(curl_handle, CURLOPT_NOPROGRESS, 0L); 1429 curl_easy_setopt(curl_handle, CURLOPT_XFERINFOFUNCTION, HttpHelper::progress_callback); 1430 curl_easy_setopt(curl_handle, CURLOPT_XFERINFODATA, thread_chunk); 1431 1432 curl_easy_setopt(curl_handle, CURLOPT_LOW_SPEED_LIMIT, 1L); 1433 curl_easy_setopt(curl_handle, CURLOPT_LOW_SPEED_TIME, 5L); 1434 1435 if (thread_chunk->_endidx != 0) 1436 { 1437 std::string down_range; 1438 std::ostringstream ostr; 1439 if (thread_chunk->_endidx > 0) 1440 { 1441 ostr << thread_chunk->_startidx << "-" << thread_chunk->_endidx; 1442 } 1443 else 1444 { 1445 ostr << thread_chunk->_startidx << "-"; 1446 } 1447 1448 down_range = ostr.str(); 1449 curl_easy_setopt(curl_handle, CURLOPT_RANGE, down_range.c_str()); 1450 } 1451 1452 CURLcode curl_code = curl_easy_perform(curl_handle); 1453 if (curl_code == CURLE_OPERATION_TIMEDOUT) 1454 { 1455 int retry_count = m_retry_times; 1456 while (retry_count > 0) 1457 { 1458 curl_code = curl_easy_perform(curl_handle); 1459 if (curl_code != CURLE_OPERATION_TIMEDOUT) break; 1460 retry_count--; 1461 } 1462 } 1463 1464 long http_code; 1465 curl_easy_getinfo(curl_handle, CURLINFO_RESPONSE_CODE, &http_code); 1466 if (curl_code == CURLE_OK && (http_code >= 200 && http_code <= 300)) 1467 { 1468 m_http_code = http_code; 1469 thread_chunk->_download->m_download_fail = false; 1470 } 1471 else 1472 { 1473 const char* err_string = curl_easy_strerror(curl_code); 1474 m_error_string = err_string; 1475 thread_chunk->_download->m_download_fail = true; 1476 m_http_code = http_code; 1477 } 1478 1479 curl_easy_cleanup(curl_handle); 1480 1481 delete thread_chunk; 1482 1483 return curl_code; 1484 } 1485 1486 int HttpDownloader::DownloadHelper::SplitDownloadCount(double down_size) 1487 { 1488 const double size_2mb = 2.0 * 1024 * 1024; 1489 const double size_10mb = 10.0 * 1024 * 1024; 1490 const double size_50mb = 50.0 * 1024 * 1024; 1491 1492 if (down_size <= size_2mb) 1493 { 1494 return 1; 1495 } 1496 else if (down_size > size_2mb && down_size <= size_10mb) 1497 { 1498 return static_cast<int>(down_size / (size_2mb)); 1499 } 1500 else if (down_size > size_10mb && down_size <= size_50mb) 1501 { 1502 return HttpDownloader::s_kThreadCount + 1; 1503 } 1504 else 1505 { 1506 int down_count = static_cast<int>(down_size / size_10mb); 1507 return down_count > 10 ? 10 : down_count; 1508 } 1509 1510 return 1; 1511 } 1512 1513 void HttpDownloader::DownloadHelper::Reset() 1514 { 1515 if (m_is_running) 1516 { 1517 return; 1518 } 1519 1520 if (m_perform_thread) //thread run over because if m_is_running set true, Reset wont be invoke 1521 { 1522 #ifdef _WIN32 1523 CloseHandle(m_perform_thread); 1524 m_perform_thread = nullptr; 1525 #endif 1526 } 1527 1528 m_close_self = false; 1529 m_multi_download = false; 1530 m_download_fail = true; 1531 m_is_running = false; 1532 m_is_cancel = false; 1533 m_http_code = 0; 1534 m_total_size = 0.0; 1535 m_downloaded_size = 0.0; 1536 1537 m_receive_header = ""; 1538 m_error_string = ""; 1539 }

    libcurl的http請求默認是Get。如果指定了Post數據,則是Post請求。

    ?

  • 使用libcurl庫
    demo使用封裝的庫來模擬請求數據和下載文件。
    例子很簡單,直接看代碼:
    1 // http_request.cpp : 定義控制臺應用程序的入口點。2 //3 4 #include "HttpRequest.h"5 6 #include <iostream>7 #include <string>8 #include <fstream>9 #include <functional>10 11 class 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下載進度%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 }

    ?

  • 轉載于:https://www.cnblogs.com/lehoho/p/9367287.html

    總結

    以上是生活随笔為你收集整理的libcurl的封装,支持同步异步请求,支持多线程下载,支持https(z)的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

    免费看十八岁美女 | 欧美大片第1页 | 在线观看av免费 | 日本在线视频一区二区三区 | 久久久国产电影 | 97人人模人人爽人人喊中文字 | 97在线看片 | 992tv又爽又黄的免费视频 | 国产精品一区二区三区在线播放 | 九九电影在线 | 嫩草av影院 | 99综合久久| 国产黄色a| 亚洲综合欧美精品电影 | 久久久久久久久久久久影院 | 日韩精品在线观看视频 | 欧美一区成人 | 一级α片 | 国产精品二区在线观看 | 六月丁香婷婷网 | 国产精品成 | 国产专区免费 | 亚洲成人资源在线观看 | 国产精品高清免费在线观看 | av网在线观看 | 99国产视频在线 | 成人久久精品视频 | www成人av| 久久久久久欧美二区电影网 | 国产精品aⅴ | 美女网站黄在线观看 | 精品国产电影 | av免费看电影 | 免费观看一区二区 | 天天曰夜夜爽 | 成人精品影视 | 久草在线免费新视频 | 国产精品va在线 | 日本视频网 | 国产探花视频在线播放 | 日韩中文字幕免费 | 国产系列在线观看 | 国产伦精品一区二区三区无广告 | 国产最新在线 | 免费日韩 | 国产高清视频色在线www | 国产精品久久久久久久久久久久午夜 | 狠狠躁夜夜躁人人爽超碰97香蕉 | 欧美一区二区在线刺激视频 | 最近日本中文字幕a | 中文字幕乱码在线播放 | 亚洲成人中文在线 | 亚洲一区二区精品视频 | 色婷婷福利视频 | 国产在线观看h | 丁香花在线观看视频在线 | 国产精品一区二区吃奶在线观看 | 最新午夜| 啪一啪在线 | 国产精品亚洲片在线播放 | 欧美三级高清 | 国产精品一区二区三区免费视频 | 国产男女无遮挡猛进猛出在线观看 | 国产香蕉久久精品综合网 | 久久久久日本精品一区二区三区 | 在线视频精品播放 | 在线观看亚洲专区 | 国产99久久久国产精品免费看 | 成人国产在线 | 天天操天天操天天操天天 | 国产精品九九视频 | 玖玖在线免费视频 | 激情五月婷婷综合 | 天天射天天操天天色 | 九九色网 | 久草视频视频在线播放 | 中文字幕在线播放av | 高清在线观看av | 亚洲综合视频网 | 日韩欧美视频在线免费观看 | 二区三区精品 | 国产成人av福利 | 五月天亚洲激情 | av电影在线不卡 | 狠狠躁夜夜躁人人爽超碰97香蕉 | 玖草在线观看 | 日韩欧美一区二区三区在线 | 欧美老人xxxx18 | 亚洲激情综合 | 五月婷婷六月丁香激情 | 日韩在线视频一区二区三区 | 国产不卡在线播放 | 一级特黄av | 粉嫩av一区二区三区四区五区 | 永久av免费在线观看 | 狠狠操操 | 99久国产 | 久久久国产精品成人免费 | 丁香婷婷久久久综合精品国产 | 日韩激情综合 | 麻豆超碰 | 国产精品99久久久久的智能播放 | 一区二区三区在线不卡 | 日韩高清免费电影 | 国产在线综合视频 | 久久综合久久综合久久综合 | 日日精品 | 欧美了一区在线观看 | 欧美午夜精品久久久久久孕妇 | 亚洲好视频 | 国产精品美女久久久久久久 | 91视频88av| 国产精品日韩欧美一区二区 | 国产高清一区二区 | 丁香激情网 | 日韩高清免费在线 | 狠狠干夜夜操天天爽 | 精品国产一区二区三区在线观看 | 超碰在线个人 | 成在人线av | 亚洲成av人片在线观看无 | 日韩一级成人av | 国产亚洲人成网站在线观看 | 国产一级片播放 | av免费看在线 | 欧美日韩久久 | 99九九视频 | 国产男男gay做爰 | 国产精品精品久久久久久 | 六月丁香久久 | 久久国内免费视频 | 91精品伦理 | 亚洲 欧美 国产 va在线影院 | 成人丁香花 | 乱子伦av| 91理论电影| 99久久99久久精品免费 | 中文字幕高清视频 | 激情 婷婷 | 成年人免费观看在线视频 | 最近中文字幕免费大全 | 中文字幕av在线电影 | 国产精品一区二区三区四 | 99久久精品一区二区成人 | 日韩免费久久 | 香蕉视频在线视频 | 国产一级淫片免费看 | 成人av一级片 | 久久爱综合 | a久久免费视频 | 久久五月婷婷丁香社区 | 亚洲成人国产精品 | 日韩在线短视频 | 香蕉在线观看视频 | 久久手机看片 | 久久久久亚洲国产 | 日韩欧美一区二区在线播放 | 精一区二区 | 久久综合导航 | 在线观看不卡的av | 手机av片| 日韩中文字幕免费看 | 日韩精品免费在线观看视频 | 91在线影视 | 亚洲精品高清在线观看 | 日日夜夜网站 | 国产91在线 | 美洲 | 日本精品视频在线观看 | 欧美一区三区四区 | 中文免费在线观看 | 亚洲涩涩网站 | 国产精品久久久久久久久费观看 | 成人免费在线观看入口 | 免费看成人片 | 国产日韩精品一区二区三区在线 | av中文字幕网站 | 色婷婷播放 | 国产亚洲小视频 | 婷婷亚洲综合五月天小说 | 久久影院中文字幕 | 在线免费观看的av网站 | a国产精品 | 手机av电影在线 | 在线91播放 | 日韩欧美电影在线观看 | 91视频麻豆 | 免费成人av在线 | 超碰97国产精品人人cao | 久久一区二区免费视频 | 一区二区电影在线观看 | 国产精品久久久久久久久久久免费 | 视频成人 | 91av视频观看 | 久久毛片网站 | 久久免费精品 | 欧美日韩免费一区 | 亚洲aaa毛片 | 免费观看一区二区三区视频 | 日韩av在线不卡 | 久久久国产精品一区二区三区 | 亚洲精品视频在线观看网站 | 日本在线视频网址 | 91精品国产成人观看 | 久久精品导航 | 久草97| 一区二区三区手机在线观看 | av在线亚洲天堂 | 中文字幕二区在线观看 | 婷婷久久一区二区三区 | 日韩精品一区二区久久 | 久久久精品免费观看 | 国产精品精品 | 欧美一二三区播放 | 午夜久久美女 | 国产一区二区高清不卡 | 成人av资源在线 | 久久国产精品影视 | 2020天天干夜夜爽 | 亚洲精品久久激情国产片 | 91视频三区 | 综合影视 | 伊人首页| 精品一区91 | 99色在线播放 | 91精品国产入口 | 中文字幕免 | 综合色亚洲 | 午夜电影久久 | 成人免费观看a | 美女视频免费一区二区 | 麻豆一区二区 | 成人在线观看资源 | 国产黄色精品在线观看 | 超碰激情在线 | 麻豆激情电影 | 久久精品国产美女 | 在线高清av | 又黄又爽的视频在线观看网站 | 黄色av在 | 久久爱导航 | 国产拍揄自揄精品视频麻豆 | 亚洲国产天堂av | 久久97久久| 欧美激情精品久久久久久变态 | 欧美日韩免费视频 | 欧美极品在线播放 | 西西人体4444www高清视频 | 欧美福利视频一区 | 久久精品最新 | 激情欧美日韩一区二区 | 99久热精品 | 亚洲精品美女久久久 | 国产黄色电影 | 国产99久久久国产精品 | 在线之家免费在线观看电影 | 午夜成人免费影院 | 久久久免费视频播放 | 91成人免费在线视频 | 久久免费视频在线观看 | 亚洲女人av | 2022国产精品视频 | 日韩在线播放av | 黄色毛片在线 | 国产日韩精品欧美 | 成人av在线影院 | 国产欧美精品在线观看 | 亚洲三级精品 | 人人干天天射 | 99热在线观看免费 | a视频在线播放 | 国产尤物在线视频 | 成年人视频在线免费观看 | 成人毛片a | 成人在线超碰 | 中文字幕.av.在线 | 国产日产高清dvd碟片 | 探花视频在线观看+在线播放 | 天天操天天干天天操天天干 | 久久精品亚洲综合专区 | 精品福利国产 | 蜜臀久久99精品久久久无需会员 | 在线观看黄 | 偷拍精品一区二区三区 | 成年人视频在线免费观看 | 日韩av不卡在线 | 欧美激情另类文学 | 国产 在线 高清 精品 | 成人免费在线播放视频 | 久久九九九九 | 国产日韩欧美在线一区 | 91福利免费 | 国产精品一区二区美女视频免费看 | 国产无遮挡猛进猛出免费软件 | 免费日韩视| 国产香蕉久久 | 中文字幕在线看视频 | av青草| 最近中文字幕视频网 | 国产精品久久久久久久久久久久冷 | 97超碰在线久草超碰在线观看 | 日本中文乱码卡一卡二新区 | 日韩视频免费观看高清完整版在线 | 日韩精品一区二区三区免费观看视频 | av在线播放免费 | 免费中午字幕无吗 | www.久久久.com| 99 国产精品| 四虎欧美 | 久久精品久久久精品美女 | 中文字幕在线观看视频免费 | 国产黄色视 | 亚洲成aⅴ人在线观看 | 久久久综合电影 | 蜜桃av久久久亚洲精品 | 97超碰在线久草超碰在线观看 | 在线观看亚洲精品 | av短片在线| av 一区二区三区四区 | 亚洲伦理一区二区 | 操一草| 婷婷激情五月 | 九九色综合 | 2023av| 久久精品专区 | 国产精品丝袜 | 日韩电影在线视频 | 婷婷在线资源 | 在线观看av网 | 在线精品亚洲一区二区 | 国产精品v欧美精品 | 亚洲精品免费在线视频 | 日韩激情中文字幕 | 欧美肥妇free | 中文字幕在线免费看线人 | 亚洲五月激情 | 黄色三级免费网址 | 久久久一本精品99久久精品66 | 麻豆 91 在线| 久久美女免费视频 | 亚洲aⅴ乱码精品成人区 | 国产精品久久久久久久久久久久久 | 日本特黄特色aaa大片免费 | 开心综合网 | 久久久久久高潮国产精品视 | 日日麻批40分钟视频免费观看 | 91久久精| 日韩av一区二区在线影视 | 黄色亚洲大片免费在线观看 | 久久香蕉一区 | www狠狠| 国产又粗又猛又爽又黄的视频免费 | 亚洲视频1区2区 | 国产三级精品三级在线观看 | 国产精品免费人成网站 | 最新日韩精品 | 网址你懂的在线观看 | 国产精品va最新国产精品视频 | 国产自偷自拍 | 国产99久久久精品 | 深夜免费小视频 | 日韩免费视频观看 | 毛片美女网站 | 天天曰天天干 | 欧美在线观看视频免费 | 亚洲国产三级在线观看 | 精品国产一区二区三区不卡 | 蜜臀av性久久久久蜜臀aⅴ涩爱 | 免费av网站观看 | 九九视频在线播放 | 91免费观看视频网站 | 91国内在线视频 | 欧美老少交 | 国产999精品久久久 免费a网站 | 四虎影视精品永久在线观看 | 国产日韩视频在线播放 | 久久er99热精品一区二区三区 | 手机av电影在线观看 | 欧美日韩1区2区 | 色综合久久久久久久 | 天天玩夜夜操 | 欧美激情视频在线免费观看 | 亚洲黄色成人av | 久久香蕉国产 | 欧美怡红院 | 午夜精品福利一区二区三区蜜桃 | 国产成人精品一区二区三区免费 | 天天做日日做天天爽视频免费 | 国产特级毛片aaaaaa | 久久国产欧美日韩精品 | 日韩av片无码一区二区不卡电影 | avav片 | 嫩草91影院| 久久一线 | 日韩在线观看免费 | 在线看片日韩 | 亚洲午夜久久久久久久久电影网 | 99热精品免费观看 | 亚洲视频 视频在线 | 国产一区二区在线精品 | 天天操天天干天天综合网 | 日韩理论视频 | 91亚洲影院 | 国产精品免费不卡 | 91一区啪爱嗯打偷拍欧美 | 中文在线字幕观看电影 | 五月天婷亚洲天综合网鲁鲁鲁 | 日韩一区二区久久 | 欧美网站黄色 | 久久在线观看视频 | 国产在线精品视频 | 五月开心激情 | 九热在线| 成人天堂网 | 国产精品18久久久 | 国产麻豆精品一区二区 | 中文字幕永久 | 中文字幕在线观看第一区 | 精品国产日本 | 午夜国产福利在线 | 最近中文字幕高清字幕在线视频 | 97国产精品 | 久久激情综合网 | 在线黄色av | 国产123区在线观看 国产精品麻豆91 | 成人9ⅰ免费影视网站 | 天天天天天天干 | 91中文视频 | 日韩精品视频在线免费观看 | 国产精品一区二区三区视频免费 | 亚洲高清在线观看视频 | 亚洲综合小说电影qvod | 韩国一区二区三区视频 | 日韩欧美69 | 久久久久电影 | 国产老太婆免费交性大片 | 中文字幕国产精品一区二区 | 99热精品国产 | 久久黄色免费观看 | 久久99久久精品国产 | 久久艹国产视频 | 日韩影视精品 | 国产黄色精品在线观看 | 日产乱码一二三区别免费 | 成人一区二区三区中文字幕 | 最近中文字幕完整高清 | 国产精品理论视频 | 日韩美在线观看 | 91在线一区二区 | 久久免费视频8 | 91丨九色丨首页 | 免费观看性生活大片3 | 色视频 在线 | 日韩精品免费在线 | 96亚洲精品久久 | 国产录像在线观看 | 中文字幕免费 | 在线播放 日韩专区 | 亚洲天堂在线观看完整版 | 精品一区二区免费视频 | 日韩电影中文字幕在线 | 久久精品美女视频网站 | 国产精品专区在线 | www色网站 | 久色免费视频 | 99久久国产免费免费 | 色婷婷国产 | 久久avav | 久久久免费观看完整版 | 丁香六月激情婷婷 | 久久91网| 人人天天夜夜 | 国产精成人品免费观看 | 色黄久久久久久 | 天天干.com | 中文字幕第一 | 色www精品视频在线观看 | 久久亚洲综合国产精品99麻豆的功能介绍 | 99精品在线观看 | 婷婷亚洲综合五月天小说 | 国产高清不卡在线 | 国内精品视频久久 | 免费在线黄色av | 精品99久久 | 欧美国产一区在线 | 五月婷婷色丁香 | 中文字幕亚洲欧美 | 国产午夜精品一区二区三区在线观看 | 久草在线视频看看 | 免费网站在线观看成人 | 午夜精品成人一区二区三区 | 亚洲黄色成人网 | 免费黄色av片| 成人一区二区在线观看 | 日韩理论视频 | 在线视频国产区 | 日本久久电影 | 97精品国产97久久久久久 | 亚洲1区 在线 | 欧美日韩精品在线免费观看 | 亚洲国产成人精品在线 | 亚洲精品美女久久 | 国产九九九视频 | 日韩夜夜爽| 日日夜精品 | 国产丝袜制服在线 | 免费看在线看www777 | 亚洲免费av一区二区 | 99精彩视频在线观看免费 | 91精品国产99久久久久久久 | 亚洲黄色三级 | 国产亚洲精品女人久久久久久 | 久久99久久精品国产 | 国产原创在线 | 亚洲毛片在线观看. | 97在线观看免费观看高清 | 又黄又爽又刺激 | 日本中文字幕在线免费观看 | 国产小视频精品 | 国产传媒一区在线 | 国内精品福利视频 | 91福利区一区二区三区 | 草久视频在线观看 | 国产精品麻豆果冻传媒在线播放 | 中文电影网 | 日韩激情小视频 | 日韩在线一级 | 四虎8848免费高清在线观看 | 丁香六月婷婷激情 | 黄色一及电影 | 成人精品久久久 | 玖草在线观看 | 成人av日韩 | 波多野结衣在线观看一区 | 日韩av在线网站 | 欧美精品在线观看一区 | 日本女人逼| 欧美另类性 | 午夜免费视频网站 | 在线精品一区二区 | 国产专区第一页 | 精品二区视频 | 亚洲va欧美va人人爽春色影视 | 亚洲国产精品久久久久久 | 国产高清在线观看av | 日韩av进入 | 91麻豆精品国产91久久久久久 | 国产精品av久久久久久无 | 亚洲女人天堂成人av在线 | 狠狠操操操 | 一区二区三区四区免费视频 | 日韩在线观 | 五月开心六月婷婷 | 美女久久久久久久久久久 | 亚洲精品视频久久 | 午夜精品久久久久久久99水蜜桃 | 久久视频国产精品免费视频在线 | 视频在线亚洲 | 成人欧美日韩国产 | 顶级欧美色妇4khd | 日本精品视频在线播放 | 免费亚洲视频 | av女优中文字幕在线观看 | 国产成人综合图片 | 91av看片| 国产九九九精品视频 | 久久久91精品国产 | 久久免费视频1 | 狠狠精品 | 91在线视频精品 | 日韩视频图片 | 国产精品久久久久久久久久99 | 国产一区二区精品91 | 欧美精品一二三 | 天天操,夜夜操 | 操碰av | 66av99精品福利视频在线 | 欧美精彩视频 | 免费视频一二三区 | 2023亚洲精品国偷拍自产在线 | 久草视频免费在线播放 | 91免费在线看片 | 色在线网站 | 免费视频你懂得 | 中文字幕黄色网 | 三级黄在线 | 国产视频丨精品|在线观看 国产精品久久久久久久久久久久午夜 | av黄色免费看 | 中文av网| 99九九视频 | 五月婷婷狠狠 | 伊人久久av | 九色福利视频 | 久久精品国产亚洲a | 麻豆免费精品视频 | 日韩在线视 | 人人草人人草 | 欧美精品中文 | 日韩a级免费视频 | 激情五月婷婷 | 精品久久一二三区 | 在线中文字幕电影 | 亚洲人成网站精品片在线观看 | 日本黄色大片免费看 | 一区二区三区中文字幕在线观看 | www.久艹 | 免费看一级黄色大全 | 天天干夜夜爱 | 最近中文字幕免费 | 精品久久久久一区二区国产 | 91九色视频在线播放 | 日本公妇在线观看 | 天天射色综合 | 九九视频免费 | 91久久一区二区 | 看国产黄色片 | 久久久久久欧美二区电影网 | 天天综合婷婷 | 久久午夜精品影院一区 | 国产青春久久久国产毛片 | 色噜噜日韩精品一区二区三区视频 | 日韩免费观看av | 玖玖爱免费视频 | 亚洲丝袜中文 | 国产精品18久久久久久不卡孕妇 | 精品女同一区二区三区在线观看 | 亚洲精品女人久久久 | 99久久99久久 | 91精品国产自产91精品 | 美女露久久 | 天天爱天天操 | www久久99| 免费观看国产精品 | 国产精品日韩精品 | 亚洲干 | 日韩黄色免费电影 | 夜夜躁日日躁 | 在线久热 | 成人va天堂 | 欧美日韩午夜爽爽 | 免费观看久久久 | 亚洲精品免费视频 | 国产小视频在线看 | 国内久久看 | 国产精品嫩草影视久久久 | 欧美人zozo | 在线看v片成人 | 91九色最新地址 | 亚洲精品字幕在线 | 国产美女免费视频 | 日本性生活一级片 | 久久精品3 | 开心婷婷色 | 日日夜夜精品视频 | 欧美日韩在线视频一区二区 | 国产一线二线三线在线观看 | 热久精品| 久久久99精品免费观看乱色 | 狠狠干网站 | 五月婷婷中文网 | 久久免费精彩视频 | 国产精品自产拍在线观看蜜 | 国产激情久久久 | 蜜臀av夜夜澡人人爽人人桃色 | 久久久免费精品 | 最近更新好看的中文字幕 | 精品国产视频在线 | 最新av电影网站 | 波多野结衣在线观看视频 | 久久久久久国产精品999 | 国产精品自产拍在线观看蜜 | 日本最新高清不卡中文字幕 | 国产h片在线观看 | 激情婷婷久久 | 久久久黄色免费网站 | 国产在线欧美日韩 | 中文一区二区三区在线观看 | 国产精品久久久久久久久久99 | 亚洲乱码久久 | 亚洲色图av| 国产在线一线 | 国产麻豆精品一区二区 | 国产精品伦一区二区三区视频 | 人九九精品 | 欧美性生交大片免网 | 日韩av影视| 久草免费在线视频观看 | 欧美一区二区三区在线 | 亚洲欧美成人网 | 蜜臀精品久久久久久蜜臀 | 国产黄色看片 | 国产精品成人免费一区久久羞羞 | 黄色一级在线免费观看 | 欧美伦理一区二区三区 | 91成人免费在线视频 | 亚洲天堂网在线播放 | 久草网在线观看 | 欧美日本啪啪无遮挡网站 | 久久国产精品久久国产精品 | 久久综合网色—综合色88 | 国产免费高清 | av免费在线播放 | 欧美精品二 | 天天想夜夜操 | 在线成人高清电影 | 在线观看91网站 | 久久少妇免费视频 | 男女精品久久 | 亚洲成a人片在线观看中文 中文字幕在线视频第一页 狠狠色丁香婷婷综合 | 久久久久久久久久久福利 | 国产在线一线 | 天天操天天操天天 | 久草免费色站 | 国产亚洲精品久久久久久移动网络 | 2018精品视频 | 操老逼免费视频 | 丁香花在线视频观看免费 | 久久成年视频 | 九九九免费视频 | 99热最新精品 | 欧美日韩不卡一区二区 | 2021久久 | 久久精品欧美日韩精品 | 国产精品国产自产拍高清av | 少妇搡bbbb搡bbb搡忠贞 | 国产精久久 | 色噜噜日韩精品一区二区三区视频 | 午夜视频在线观看一区二区 | 国产色婷婷精品综合在线手机播放 | 国产又粗又硬又长又爽的视频 | 欧美日韩xxxxx | 久久成人高清视频 | 99国产免费网址 | 亚洲精品成人在线 | 日韩毛片精品 | 在线观看视频一区二区 | 500部大龄熟乱视频 欧美日本三级 | 九九久久成人 | 国产精品视频永久免费播放 | 久久久999免费视频 日韩网站在线 | 国产不卡一 | 国产精品videossex国产高清 | 黄色tv视频| 亚洲精品字幕在线 | 九九九在线观看视频 | 在线观看黄色小视频 | 极品美女被弄高潮视频网站 | 伊人五月天.com | 97成人免费 | 天天综合网天天综合色 | 久久久久一区二区三区 | 亚洲欧洲精品久久 | 色在线亚洲| 国产成人精品一区二三区 | 久草在线99 | 久久久久久久久网站 | 深夜精品福利 | 在线观看免费福利 | www国产精品com | 国产精品免费视频观看 | 国产精品欧美一区二区 | 中文字幕区 | 免费网站v | 国产h在线观看 | 精品电影一区二区 | 欧美日韩亚洲精品在线 | av中文字幕网站 | 国产精品久久人 | 中文字幕乱在线伦视频中文字幕乱码在线 | 欧美日韩不卡在线视频 | www.av免费 | 国产精品女同一区二区三区久久夜 | 亚洲国产中文字幕在线观看 | 国产日韩av在线 | 色欧美日韩 | 亚洲黄色精品 | 国产一级精品绿帽视频 | 亚洲精品在线观看视频 | 高清免费在线视频 | 四虎成人在线 | 久久在现 | 久久免费视频99 | 看v片| 欧美日韩一二三四区 | 色婷婷色 | 日日操天天操夜夜操 | 久久综合狠狠综合 | 99精品国产一区二区三区不卡 | 日日干日日色 | 鲁一鲁影院 | 麻豆影音先锋 | 国产成人精品一区二区三区福利 | 一区二区三区免费网站 | 成人精品久久 | 国产一区麻豆 | 久久久久亚洲最大xxxx | 黄色一级在线免费观看 | 日韩一区视频在线 | 欧美日韩免费在线视频 | 久久久久久久久艹 | 高潮毛片无遮挡高清免费 | 亚州精品国产 | av免费高清观看 | 91av在线免费看 | 成人黄色大片在线免费观看 | 亚洲精品免费在线播放 | 国产在线国偷精品产拍 | 婷婷丁香在线视频 | 亚洲精品高清视频在线观看 | 最新日韩在线观看 | 久久亚洲婷婷 | 黄a在线看 | 久草在线免费看视频 | 久久久久 免费视频 | 久久精品中文字幕一区二区三区 | 97在线看 | 操操操夜夜操 | 久久久69 | 免费av网站在线 | 日韩中文字幕91 | 永久免费看av | 国产精品99久久久久久久久 | 免费观看版 | 国产剧情在线一区 | av中文电影| 亚洲成人黄色 | 亚洲精品va| 国产成人精品av在线 | 久久久私人影院 | 国产一区二区高清不卡 | 激情五月av| 日韩欧美99 | 在线观看免费国产小视频 | 国产99爱 | 精品国产片 | 国产只有精品 | 亚洲视频 视频在线 | 亚洲天堂精品 | 婷婷成人亚洲综合国产xv88 | 在线国产视频观看 | 精品国产色 | 久久 一区| 久久黄色成人 | 中文av字幕在线观看 | 久久综合久久久久88 | 天天综合久久综合 | 四虎国产永久在线精品 | 久章操 | 人人射人人爽 | 99精品国产福利在线观看免费 | 五月天久久精品 | 亚洲黄色在线观看 | 国产片网站| 在线播放你懂 | 欧美日本不卡 | 国产一区二区在线免费播放 | 日韩欧美一区二区三区视频 | 亚洲精品91天天久久人人 | 亚洲精品高清视频 | 不卡av电影在线 | 国产视频综合在线 | 国产高清免费在线播放 | 在线免费观看涩涩 | 亚洲男人天堂a | 日韩三级视频在线观看 | www.久久com | 在线观看日韩精品 | 色干综合 | 久久99久久99 | 精品国产视频在线观看 | 久久免费视频这里只有精品 | 日韩欧美高清在线观看 | 亚洲精品国产精品国 | 国产婷婷久久 | 日韩av有码在线 | 久久人人精品 | 天天爽人人爽夜夜爽 | 岛国av在线不卡 | 国产精品久久久久亚洲影视 | 国产女人40精品一区毛片视频 | 婷婷在线免费视频 | 欧美粗又大 | 在线看一级片 | 天堂网一区二区 | 亚洲成人国产 | 国产小视频在线免费观看视频 | 国产精品免费观看视频 | 国产精品青青 | 免费中文字幕 | 中文亚洲欧美日韩 | 日韩免费小视频 | 999视频在线播放 | 中文字幕在线观看第二页 | 色婷婷www | 五月婷婷在线综合 | 欧美久草视频 | 亚洲情婷婷 | 99九九免费视频 | 五月婷婷操 | 激情视频免费在线观看 | 久久伊人五月天 | 久久综合久久综合这里只有精品 | 日韩精品一区二区电影 | 国产精品专区在线 | 最新av电影网址 | 欧美激情第一页xxx 午夜性福利 | 午夜在线免费观看 | 色一级片| 综合亚洲视频 | 国产丝袜 | av中文在线观看 | 久久久久久久18 | 国产在线a免费观看 | 亚洲激情网站免费观看 | 国产欧美精品xxxx另类 | 九九免费观看全部免费视频 | 日韩精品视频第一页 | 在线观看一 | 人人草人 | 91视频3p| 97成人精品视频在线播放 | 国产在线高清 | 亚洲欧美日韩精品久久久 | 日韩av高潮 | 深爱激情婷婷网 | 亚洲精品tv久久久久久久久久 | 国产护士av | 国产精品一区二区三区久久 | 亚洲成人第一区 | 青青河边草免费视频 | 久久精品毛片 | 国产老太婆免费交性大片 | 99精品电影 | 国产1区2区3区精品美女 | 91精品国产福利 | 久久精品久久综合 | 国产三级精品在线 | 成在线播放 | 久久久久国产精品免费 | 综合网天天射 | 在线三级av| 中字幕视频在线永久在线观看免费 | 亚洲综合最新在线 | 亚洲高清视频在线观看免费 | 在线小视频 | 2024av在线播放 | 亚洲 精品在线视频 | 久久久在线视频 | 欧美国产日韩激情 | 国产精品99久久99久久久二8 | 美女久久久久久久 | 骄小bbw搡bbbb揉bbbb | 国产精品久久在线观看 | 久久免费黄色 | 久久人人爽爽人人爽人人片av | 久久久久久久久久久久久影院 | 久久久久久久久久免费 | 正在播放国产一区二区 | 人人插人人射 | 国产成人一区二区三区免费看 | 97操操操| 西西444www大胆高清图片 | 亚洲欧美日韩一二三区 | 免费人成网 | www.天天射.com | 国产视频二区三区 | 亚洲国产精品一区二区久久,亚洲午夜 | 丝袜av网站 | 日本久久久久久科技有限公司 | 一本色道久久精品 | 国产精品成人久久久 | 国产精品18久久久久久久久 | 免费男女羞羞的视频网站中文字幕 | 日本资源中文字幕在线 | 9ⅰ精品久久久久久久久中文字幕 | 少妇bbw撒尿 | 欧美性生活免费 | 精品国产一区二区三区免费 | 日韩中文字幕视频在线观看 | 久久狠狠亚洲综合 | 激情综合五月 | 精品福利在线观看 | 日韩电影一区二区三区 | 欧美va天堂va视频va在线 | 狠狠做深爱婷婷综合一区 | 欧美一区免费在线观看 | 狠狠狠干| 视色网站| 日本中文字幕一二区观 | 欧美日韩不卡一区二区三区 | 国产a免费 | 久久久久久久久免费 | 国产精品一区二区吃奶在线观看 | 日韩高清精品一区二区 | 91精品国自产在线观看欧美 | 天天色天天色天天色 | 日韩在线免费观看视频 | 日韩精品欧美专区 | 色婷婷亚洲婷婷 |