| 注册
请输入搜索内容

热门搜索

Java Linux MySQL PHP JavaScript Hibernate jQuery Nginx
rbyt
9年前发布

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

最近在做一个项目,需要用到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并且编译
配置和编译脚本:
#!/bin/bash  # Cross-compile environment for Android on ARMv7 and x86  #  # Contents licensed under the terms of the OpenSSL license  # http://www.openssl.org/source/license.html  #  # See http://wiki.openssl.org/index.php/FIPS_Library_and_Android  #   and http://wiki.openssl.org/index.php/Android    #####################################################################    # Set ANDROID_NDK_ROOT to you NDK location. For example,  # /opt/android-ndk-r8e or /opt/android-ndk-r9. This can be done in a  # login script. If ANDROID_NDK_ROOT is not specified, the script will  # try to pick it up with the value of _ANDROID_NDK_ROOT below. If  # ANDROID_NDK_ROOT is set, then the value is ignored.  # _ANDROID_NDK="android-ndk-r8e"  #_ANDROID_NDK="android-ndk-r9"  _ANDROID_NDK="android-ndk-r10"  ANDROID_NDK_ROOT=$HOME/ndk/android-ndk-r10d  # Set _ANDROID_EABI to the EABI you want to use. You can find the  # list in $ANDROID_NDK_ROOT/toolchains. This value is always used.  # _ANDROID_EABI="x86-4.6"  # _ANDROID_EABI="arm-linux-androideabi-4.6"  _ANDROID_EABI="arm-linux-androideabi-4.8"  export ROOTDIR="${PWD}"    # Set _ANDROID_ARCH to the architecture you are building for.  # This value is always used.  # _ANDROID_ARCH=arch-x86  _ANDROID_ARCH=arch-arm    # Set _ANDROID_API to the API you want to use. You should set it  # to one of: android-14, android-9, android-8, android-14, android-5  # android-4, or android-3. You can't set it to the latest (for  # example, API-17) because the NDK does not supply the platform. At  # Android 5.0, there will likely be another platform added (android-22?).  # This value is always used.  # _ANDROID_API="android-14"  # _ANDROID_API="android-18"  # _ANDROID_API="android-19"  _ANDROID_API="android-5"    #####################################################################    # If the user did not specify the NDK location, try and pick it up.  # We expect something like ANDROID_NDK_ROOT=/opt/android-ndk-r8e  # or ANDROID_NDK_ROOT=/usr/local/android-ndk-r8e.    if [ -z "$ANDROID_NDK_ROOT" ]; then      _ANDROID_NDK_ROOT=""    if [ -z "$_ANDROID_NDK_ROOT" ] && [ -d "/usr/local/$_ANDROID_NDK" ]; then      _ANDROID_NDK_ROOT="/usr/local/$_ANDROID_NDK"    fi      if [ -z "$_ANDROID_NDK_ROOT" ] && [ -d "/opt/$_ANDROID_NDK" ]; then      _ANDROID_NDK_ROOT="/opt/$_ANDROID_NDK"    fi      if [ -z "$_ANDROID_NDK_ROOT" ] && [ -d "$HOME/$_ANDROID_NDK" ]; then      _ANDROID_NDK_ROOT="$HOME/$_ANDROID_NDK"    fi      if [ -z "$_ANDROID_NDK_ROOT" ] && [ -d "$PWD/$_ANDROID_NDK" ]; then      _ANDROID_NDK_ROOT="$PWD/$_ANDROID_NDK"    fi      # If a path was set, then export it    if [ ! -z "$_ANDROID_NDK_ROOT" ] && [ -d "$_ANDROID_NDK_ROOT" ]; then      export ANDROID_NDK_ROOT="$_ANDROID_NDK_ROOT"    fi  fi    # Error checking  # ANDROID_NDK_ROOT should always be set by the user (even when not running this script)  # http://groups.google.com/group/android-ndk/browse_thread/thread/a998e139aca71d77  if [ -z "$ANDROID_NDK_ROOT" ] || [ ! -d "$ANDROID_NDK_ROOT" ]; then    echo "Error: ANDROID_NDK_ROOT is not a valid path. Please edit this script."    # echo "$ANDROID_NDK_ROOT"    # exit 1  fi    # Error checking  if [ ! -d "$ANDROID_NDK_ROOT/toolchains" ]; then    echo "Error: ANDROID_NDK_ROOT/toolchains is not a valid path. Please edit this script."    # echo "$ANDROID_NDK_ROOT/toolchains"    # exit 1  fi    # Error checking  if [ ! -d "$ANDROID_NDK_ROOT/toolchains/$_ANDROID_EABI" ]; then    echo "Error: ANDROID_EABI is not a valid path. Please edit this script."    # echo "$ANDROID_NDK_ROOT/toolchains/$_ANDROID_EABI"    # exit 1  fi    #####################################################################    # Based on ANDROID_NDK_ROOT, try and pick up the required toolchain. We expect something like:  # /opt/android-ndk-r83/toolchains/arm-linux-androideabi-4.7/prebuilt/linux-x86_64/bin  # Once we locate the toolchain, we add it to the PATH. Note: this is the 'hard way' of  # doing things according to the NDK documentation for Ice Cream Sandwich.  # https://android.googlesource.com/platform/ndk/+/ics-mr0/docs/STANDALONE-TOOLCHAIN.html    ANDROID_TOOLCHAIN=""  for host in "linux-x86_64" "linux-x86" "darwin-x86_64" "darwin-x86"  do    if [ -d "$ANDROID_NDK_ROOT/toolchains/$_ANDROID_EABI/prebuilt/$host/bin" ]; then      ANDROID_TOOLCHAIN="$ANDROID_NDK_ROOT/toolchains/$_ANDROID_EABI/prebuilt/$host/bin"      break    fi  done    # Error checking  if [ -z "$ANDROID_TOOLCHAIN" ] || [ ! -d "$ANDROID_TOOLCHAIN" ]; then    echo "Error: ANDROID_TOOLCHAIN is not valid. Please edit this script."    # echo "$ANDROID_TOOLCHAIN"    # exit 1  fi    case $_ANDROID_ARCH in      arch-arm)              ANDROID_TOOLS="arm-linux-androideabi-gcc arm-linux-androideabi-ranlib arm-linux-androideabi-ld"        ;;      arch-x86)              ANDROID_TOOLS="i686-linux-android-gcc i686-linux-android-ranlib i686-linux-android-ld"        ;;            *)        echo "ERROR ERROR ERROR"        ;;  esac    for tool in $ANDROID_TOOLS  do    # Error checking    if [ ! -e "$ANDROID_TOOLCHAIN/$tool" ]; then      echo "Error: Failed to find $tool. Please edit this script."      # echo "$ANDROID_TOOLCHAIN/$tool"      # exit 1    fi  done    # Only modify/export PATH if ANDROID_TOOLCHAIN good  if [ ! -z "$ANDROID_TOOLCHAIN" ]; then    export ANDROID_TOOLCHAIN="$ANDROID_TOOLCHAIN"    export PATH="$ANDROID_TOOLCHAIN":"$PATH"  fi    #####################################################################    # For the Android SYSROOT. Can be used on the command line with --sysroot  # https://android.googlesource.com/platform/ndk/+/ics-mr0/docs/STANDALONE-TOOLCHAIN.html  export ANDROID_SYSROOT="$ANDROID_NDK_ROOT/platforms/$_ANDROID_API/$_ANDROID_ARCH"  export SYSROOT="$ANDROID_SYSROOT"  export NDK_SYSROOT="$ANDROID_SYSROOT"    # Error checking  if [ -z "$ANDROID_SYSROOT" ] || [ ! -d "$ANDROID_SYSROOT" ]; then    echo "Error: ANDROID_SYSROOT is not valid. Please edit this script."    # echo "$ANDROID_SYSROOT"    # exit 1  fi    #####################################################################    # If the user did not specify the FIPS_SIG location, try and pick it up  # If the user specified a bad location, then try and pick it up too.  if [ -z "$FIPS_SIG" ] || [ ! -e "$FIPS_SIG" ]; then      # Try and locate it    _FIPS_SIG=""    if [ -d "/usr/local/ssl/$_ANDROID_API" ]; then      _FIPS_SIG=`find "/usr/local/ssl/$_ANDROID_API" -name incore`    fi      if [ ! -e "$_FIPS_SIG" ]; then      _FIPS_SIG=`find $PWD -name incore`    fi      # If a path was set, then export it    if [ ! -z "$_FIPS_SIG" ] && [ -e "$_FIPS_SIG" ]; then      export FIPS_SIG="$_FIPS_SIG"    fi  fi    # Error checking. Its OK to ignore this if you are *not* building for FIPS  if [ -z "$FIPS_SIG" ] || [ ! -e "$FIPS_SIG" ]; then    echo "Error: FIPS_SIG does not specify incore module. Please edit this script."    # echo "$FIPS_SIG"    # exit 1  fi    #####################################################################    # Most of these should be OK (MACHINE, SYSTEM, ARCH). RELEASE is ignored.  export MACHINE=armv7  export RELEASE=2.6.37  export SYSTEM=android  export ARCH=arm  export CROSS_COMPILE="arm-linux-androideabi-"    if [ "$_ANDROID_ARCH" == "arch-x86" ]; then      export MACHINE=i686      export RELEASE=2.6.37      export SYSTEM=android      export ARCH=x86      export CROSS_COMPILE="i686-linux-android-"  fi    # For the Android toolchain  # https://android.googlesource.com/platform/ndk/+/ics-mr0/docs/STANDALONE-TOOLCHAIN.html  export ANDROID_SYSROOT="$ANDROID_NDK_ROOT/platforms/$_ANDROID_API/$_ANDROID_ARCH"  export SYSROOT="$ANDROID_SYSROOT"  export NDK_SYSROOT="$ANDROID_SYSROOT"  export ANDROID_NDK_SYSROOT="$ANDROID_SYSROOT"  export ANDROID_API="$_ANDROID_API"    # CROSS_COMPILE and ANDROID_DEV are DFW (Don't Fiddle With). Its used by OpenSSL build system.  # export CROSS_COMPILE="arm-linux-androideabi-"  export ANDROID_DEV="$ANDROID_NDK_ROOT/platforms/$_ANDROID_API/$_ANDROID_ARCH/usr"  export HOSTCC=gcc    VERBOSE=1  if [ ! -z "$VERBOSE" ] && [ "$VERBOSE" != "0" ]; then    echo "ANDROID_NDK_ROOT: $ANDROID_NDK_ROOT"    echo "ANDROID_ARCH: $_ANDROID_ARCH"    echo "ANDROID_EABI: $_ANDROID_EABI"    echo "ANDROID_API: $ANDROID_API"    echo "ANDROID_SYSROOT: $ANDROID_SYSROOT"    echo "ANDROID_TOOLCHAIN: $ANDROID_TOOLCHAIN"    echo "FIPS_SIG: $FIPS_SIG"    echo "CROSS_COMPILE: $CROSS_COMPILE"    echo "ANDROID_DEV: $ANDROID_DEV"  fi    cd openssl  if [ $# -gt 0 ]; then  perl -pi -e 's/install: all install_docs install_sw/install: install_docs install_sw/g' Makefile.org  ./config -DOPENSSL_NO_HEARTBEATS no-shared no-ssl2 no-ssl3 no-comp no-hw no-engine --openssldir=${ROOTDIR}/build/openssl  fi  make depend  make && make install    openssl configure

1.2 配置zlib并且编译
配置脚本:
#!/bin/sh    export ROOTDIR="${PWD}"  cd zlib/    export CROSS_COMPILE="arm-linux-androideabi"  export CPPFLAGS="-fPIC"  export CFLAGS="-fPIC"  export AR=${CROSS_COMPILE}-ar  export AS=${CROSS_COMPILE}-as  export LD=${CROSS_COMPILE}-ld  export RANLIB=${CROSS_COMPILE}-ranlib  export CC=${CROSS_COMPILE}-gcc  export CXX=${CROSS_COMPILE}-g++  export NM=${CROSS_COMPILE}-nm    ./configure --prefix=${ROOTDIR}/build/zlib --static    zlib configure

配置成功之后,cd进代码目录执行make && make install命令即可

1.3 配置libcurl并且编译

配置脚本:
#!/bin/sh    export ROOTDIR="${PWD}"  cd curl-7.42.1/    export CROSS_COMPILE="arm-linux-androideabi"  export CPPFLAGS="-fPIC -I${ROOTDIR}/build/openssl/include -I${ROOTDIR}/build/zlib/include"  export CFLAGS="-fPIC -I${ROOTDIR}/build/openssl/include -I${ROOTDIR}/build/zlib/include"    export LDFLAGS="-L${ROOTDIR}/build/openssl/lib -L${ROOTDIR}/build/zlib/lib"  export LIBS="-lssl -lcrypto -lz"    export AR=${CROSS_COMPILE}-ar  export AS=${CROSS_COMPILE}-as  export LD=${CROSS_COMPILE}-ld  export RANLIB=${CROSS_COMPILE}-ranlib  export CC=${CROSS_COMPILE}-gcc  export CXX=${CROSS_COMPILE}-g++  export NM=${CROSS_COMPILE}-nm    ./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    libcurl configure

配置成功之后,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
头文件:
#ifndef __HTTP_REQUEST_H  #define __HTTP_REQUEST_H      #include <string>  #include <map>  #include <memory>  #include <functional>  #include <vector>    //************************************  // Usage:      // class MyResultClass  // {  // public:  //     MyResultClass() : m_request_finished(false) { }  //     ~MyResultClass() { }  //   // public:  //     void MyRequestResultCallback(int id, bool success, const std::string& data)  //     {  //       if (success)  //       {  //        std::ofstream outfile;  //        outfile.open("baidu.html", std::ios_base::binary | std::ios_base::trunc);  //        if (outfile.good()) outfile.write(data.c_str(), data.size());  //       }  //       m_request_finished = true;  //     }  //     bool IsRequestFinish(void) { return m_request_finished; }  // private:  //     bool m_request_finished;  // };  //  // MyResultClass mc;  // HttpRequest request;  // request.SetRequestUrl("http://www.baidu.com");  // request.SetResultCallback(std::bind(&MyResultClass::MyRequestResultCallback, &mc, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));  // request.SetRequestHeader("User-Agent:Mozilla/4.04[en](Win95;I;Nav)");  // HANDLE hRequest = request.PerformRequest(HttpRequest::REQUEST_ASYNC);  // if (hRequest)  // {  //     while (mc.IsRequestFinish() == false) Sleep(300);  //     long http_code;  //     if (request.GetHttpCode(hRequest, &http_code))  //       std::cout << "http code: " << http_code << std::endl;  //     std::string header;  //     if (request.GetReceiveHeader(hRequest, &header))  //       std::cout << header << std::endl;  //     HttpRequest::Close(hRequest);  // }  // /*recommended HttpRequest::Close(hRequest) while doing async request job and dont need request handle anymore*/  //************************************    class HttpLock;    #ifndef _WIN32  typedef void* HANDLE;  #endif    class HttpRequest  {  public:      typedef enum {          REQUEST_SYNC,          REQUEST_ASYNC,      }RequestType;        typedef enum {          REQUEST_OK,          REQUEST_INVALID_OPT,          REQUEST_PERFORM_ERROR,          REQUEST_OPENFILE_ERROR,          REQUEST_INIT_ERROR,      }RequestResult;        //int id, bool success, const std::string& data      typedef std::function<void(int, bool, const std::string&)> ResultCallback;        friend class HttpHelper;        HttpRequest();      ~HttpRequest();              int SetRetryTimes(int retry_times = s_kRetryCount);      int SetRequestId(int id);      int SetRequestTimeout(long time_out = 0);      int SetRequestUrl(const std::string& url);        //************************************      // Method:    SetMovedUrl      // FullName:  HttpRequest::SetMovedUrl      // Access:    public       // Returns:   int      // Description: set http redirect follow location      // Parameter: bool get_moved_url -- true means redirect http url      //************************************      int SetMovedUrl(bool get_moved_url);        int SetPostData(const std::string& message);      int SetPostData(const void* data, unsigned int size);        //************************************      // Method:    SetRequestHeader      // FullName:  HttpRequest::SetRequestHeader      // Access:    public       // Returns:   int      // Description: set http request header, for example : Range:bytes=554554-       // Parameter: std::map<std::string, std::string>&      // Parameter: std::string> & headers      //************************************      int SetRequestHeader(std::map<std::string, std::string>& headers);      int SetRequestHeader(const std::string& header);        int SetRequestProxy(const std::string& proxy, long proxy_port);          int SetResultCallback(ResultCallback rc);        HANDLE PerformRequest(RequestType request_type);      static void Close(HANDLE request_handle);        bool GetHttpCode(HANDLE request_handle, long* http_code);      bool GetReceiveHeader(HANDLE request_handle, std::string* header);      bool GetReceiveContent(HANDLE request_handle, std::string* receive);      bool GetErrorString(HANDLE request_handle, std::string* error_string);    protected:        class RequestHelper {      public:          RequestHelper();          ~RequestHelper();            friend class HttpRequest;          friend class HttpHelper;            int      SetRetryTimes(int retry_times) { m_retry_times = retry_times; return REQUEST_OK; }            int      SetRequestTimeout(long time_out = 0);          int      SetRequestUrl(const std::string& url);          int      SetMovedUrl(bool get_moved_url);          int      SetPostData(const void* data, unsigned int size);          int      SetRequestHeader(const std::string& header);          int      SetRequestProxy(const std::string& proxy, long proxy_port);            int      SetResultCallback(ResultCallback rc);            int      Perform();            long     GetHttpCode() { return m_http_code; }          bool     GetHeader(std::string* header);          bool     GetContent(std::string* receive);          bool     GetErrorString(std::string* error_string);            bool     SelfClose(void) { return m_close_self; }        protected:          void    ReqeustResultDefault(int id, bool success, const std::string& data);        private:          HANDLE       m_curl_handle;          HANDLE       m_http_headers;  #ifdef _WIN32          HANDLE       m_perform_thread;  #else          pthread_t    m_perform_thread;  #endif            int         m_retry_times;          int         m_id;          bool        m_close_self;          bool        m_is_running;          long        m_http_code;            std::string     m_receive_content;          std::string     m_receive_header;          std::string     m_error_string;          char*               m_post_data;            ResultCallback  m_result_callback;      };    private:      std::shared_ptr<RequestHelper>  m_request_handle;      static const int               s_kRetryCount = 3;  };    //************************************  // Usage:    HttpDownloader  // class DownCallbackClass  // {  // public:  //     DownCallbackClass() :m_down_finished(false) {}  //     ~DownCallbackClass() {}  // public:  //     void DownResultCallback(int id, bool success, const std::string& data)  //     {  //       m_down_finished = true;  //     }  //     int down_callback(double total_size, double downloaded_size, void* userdata)  //     {  //       long tmp = static_cast<long>(downloaded_size / total_size * 100);  //      printf("\r下载进度%d", tmp);  //      return 0;  //     }  //     bool IsDownFinished(void) { return m_down_finished; }  // private:  //     bool m_down_finished;  // };  // HttpDownloader download;  // DownCallbackClass dc;  // const char* down_url = "http://dlsw.baidu.com/sw-search-sp/soft/71/10998/OfflineBaiduPlayer_151_V4.1.2.263.1432003947.exe";  // const char* down_file = "BaiduPlayer.exe";  //   // download.SetDownloadUrl(down_url);  // download.SetProgressCallback(std::bind(&DownCallbackClass::down_callback, &dc, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));  // download.SetResultCallback(std::bind(&DownCallbackClass::DownResultCallback, &dc, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));  // download.DownloadFile(down_file);  // HANDLE hDownload = download.StartDownload(HttpDownloader::DOWN_ASYNC);  // if (hDownload)  // {  //     while (dc.IsDownFinished() == false) Sleep(300);   //     //to do download finish clean up  //     HttpDownloader::Close(hDownload);  // }  //************************************    class HttpDownloader  {  public:      typedef enum {          DOWN_SYNC,          DOWN_ASYNC,      }DownType;        //double total_size, double downloaded_size, void* userdata      typedef std::function<int(double, double, void*)> ProgressCallback;      //int id, bool success, const std::string& data      typedef std::function<void(int, bool, const std::string&)> ResultCallback;        friend class HttpHelper;        HttpDownloader();      ~HttpDownloader();        int         SetRequestProxy(const std::string& proxy, long proxy_port);      int         SetRetryTimes(int retry_times = s_kRetryCount);      int         SetTimeout(long time_out = 0);      int         SetDownloadUrl(const std::string& url);      int         SetUserData(void* userdata);      int         SetRequestId(int id);      int         SetProgressCallback(ProgressCallback pc);      int         SetResultCallback(ResultCallback rc);        int         DownloadFile(const std::string& file_name, int thread_count = 5);      HANDLE      StartDownload(DownType down_type);      static bool CancelDownload(HANDLE handle);      static void Close(HANDLE handle);        bool        GetHttpCode(HANDLE handle, long* http_code);      bool        GetReceiveHeader(HANDLE handle, std::string* header);      bool        GetErrorString(HANDLE handle, std::string* error_string);      void*       GetUserData(HANDLE handle);    protected:        class DownloadHelper {      public:          typedef struct tThreadChunk          {              FILE*       _fp;              long        _startidx;              long        _endidx;                DownloadHelper*     _download;          }ThreadChunk;            DownloadHelper();          ~DownloadHelper();            friend class HttpDownloader;          friend class HttpHelper;          friend ThreadChunk;            void     SetRetryTimes(int retry_times) { m_retry_times = retry_times; }          void      SetRequestId(int id) { m_id = id;  }          int      SetTimeout(long time_out = 0);          int      SetRequestUrl(const std::string& url);          int      SetRequestProxy(const std::string& proxy, long proxy_port);            void     SetUserData(void *userdata) { m_userdata = userdata; }          int      SetProgressCallback(ProgressCallback pc);          int      SetResultCallback(ResultCallback rc);          int      SetDownloadFile(const std::string& file_name);          int      SetDownloadThreadCount(int thread_count);            int      Perform();            int      GetHttpCode() { return m_http_code; }          bool     GetHeader(std::string* header);          bool     GetErrorString(std::string* error_string);          bool     SelfClose(void) { return m_close_self; }          void*    GetUserData(void) { return m_userdata; }        protected:          int      DownloadDefaultCallback(double total_size, double downloaded_size, void* userdata);          void     ResultDefaultCallback(int id, bool success, const std::string& data);          double   GetDownloadFileSize();          int      DoDownload(ThreadChunk* thread_chunk);          int      SplitDownloadCount(double down_size);        private:  #ifdef _WIN32          HANDLE        m_perform_thread;  #else          pthread_t     m_perform_thread;  #endif            int          m_retry_times;          int          m_thread_count;          int          m_id;          long         m_time_out;            std::string  m_file_path;          std::string  m_url;          std::string  m_http_proxy;          std::string  m_receive_header;          std::string  m_error_string;            bool          m_close_self;          bool            m_multi_download;          bool         m_download_fail;          bool          m_is_running;          bool         m_is_cancel;          void*        m_userdata;          long         m_http_code;          long         m_proxy_port;          double       m_total_size;          double       m_downloaded_size;            std::shared_ptr<HttpLock> m_httplock;          ProgressCallback  m_download_callback;          ResultCallback    m_result_callback;      };    private:      std::shared_ptr<DownloadHelper>    m_request_handle;        static const int          s_kRetryCount = 3;      static const int          s_kThreadCount = 4;  };    #endif  /*__HTTP_REQUEST_H*/    HttpRequest.h

实现文件:
//created by carbon @ 2015-05-29  /*                     _ooOoo_                    o8888888o                    88" . "88                    (| -_- |)                    O\  =  /O                  ___/`---'\____              .'  \\|      |//  `.              /  \\|||  :  |||//  \             /  _||||| -:- |||||-  \             |   | \\\  -  /// |   |             | \_|  ''\---/''  |   |             \  .-\__  `-`  ___/-. /           ___`. .'  /--.--\  `. . __        ."" '<  `.___\_<|>_/___.'  >'"".       | | :  `- \`.;`\ _ /`;.`/ - ` : | |       \  \ `-.   \_ __\ /__ _/   .-` /  /  ======`-.____`-.___\_____/___.-`____.-'======                     `=---='  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^       佛祖保佑    永无BUG  */  #ifdef _WIN32  #include "stdafx.h"  #else  #include <pthread.h>  #include <stdio.h>  #include <unistd.h>  #endif    #include "HttpRequest.h"   //HttpRequest class  #include "curl/curl.h"    //libcurl interface    #include <list>  #include <regex>  #include <sstream>      #ifndef _WIN32  typedef unsigned long DWORD;  #define INVALID_HANDLE_VALUE    (void*)0xffffffff  #define TRUE    1  #define FALSE   0  #endif  //#ifndef _WIN32    class HttpLock  {  public:  #ifdef _WIN32      HttpLock() { InitializeCriticalSection(&_cs); }      ~HttpLock() { DeleteCriticalSection(&_cs); }        void Lock() { EnterCriticalSection(&_cs); }      void UnLock() { LeaveCriticalSection(&_cs); }  #else      HttpLock() { pthread_mutex_init(&_lock, NULL); }      ~HttpLock() { pthread_mutex_destroy(&_lock); }        int Lock(){ return pthread_mutex_lock(&_lock); }      int UnLock() { return pthread_mutex_unlock(&_lock); }  #endif    private:  #ifdef _WIN32      CRITICAL_SECTION _cs;  #else      pthread_mutex_t    _lock;  #endif  };    class HttpHelper {  protected:      HttpHelper()      {          curl_global_init(CURL_GLOBAL_DEFAULT);            s_share_handle = curl_share_init();          curl_share_setopt(s_share_handle, CURLSHOPT_SHARE, CURL_LOCK_DATA_DNS);      }    public:      ~HttpHelper()      {          curl_share_cleanup(s_share_handle);          curl_global_cleanup();            s_async_requests.clear();          s_async_downloads.clear();      }        static HttpHelper& Instance()      {          static HttpHelper the_single_instance;          s_id++;          return the_single_instance;      }        static void set_share_handle(CURL* curl_handle)      {          curl_easy_setopt(curl_handle, CURLOPT_SHARE, s_share_handle);          curl_easy_setopt(curl_handle, CURLOPT_DNS_CACHE_TIMEOUT, 60 * 5);      }        static std::list< std::shared_ptr<HttpRequest::RequestHelper> > s_async_requests;      static std::list< std::shared_ptr<HttpDownloader::DownloadHelper> > s_async_downloads;        static int s_id;      static HttpLock       s_request_lock;      static HttpLock       s_download_lock;      static CURLSH*        s_share_handle;    #ifdef _WIN32      static DWORD WINAPI RequestThread(LPVOID param)  #else      static void* RequestThread(void* param)  #endif      {  #ifdef _WIN32          Sleep(10);  #else          usleep(10 * 1000);  #endif            std::shared_ptr<HttpRequest::RequestHelper>* request = reinterpret_cast<std::shared_ptr<HttpRequest::RequestHelper>*>(param);            if (request)          {              (*request)->Perform();              if ((*request)->SelfClose())              {                  s_request_lock.Lock();                  HttpHelper::s_async_requests.remove(*request);                  s_request_lock.UnLock();              }            }    #ifdef _WIN32          return 1;  #else          return NULL;  #endif      }        static size_t RetriveHeaderFunction(void *ptr, size_t size, size_t nmemb, void *stream)      {          std::string* receive_header = reinterpret_cast<std::string*>(stream);          if (receive_header && ptr)          {              receive_header->append(reinterpret_cast<const char*>(ptr), size * nmemb);          }            return nmemb * size;      }        static size_t RetriveContentFunction(void *ptr, size_t size, size_t nmemb, void *stream)      {          std::string* receive_content = reinterpret_cast<std::string*>(stream);          if (receive_content && ptr)          {              receive_content->append(reinterpret_cast<const char*>(ptr), size * nmemb);          }            return nmemb * size;      }    #ifdef _WIN32      static DWORD WINAPI DownloadThread(LPVOID param)  #else      static void* DownloadThread(void* param)  #endif      {  #ifdef _WIN32          Sleep(10);  #else          usleep(10 * 1000);  #endif            std::shared_ptr<HttpDownloader::DownloadHelper>* request = reinterpret_cast<std::shared_ptr<HttpDownloader::DownloadHelper>*>(param);            if (request)          {              (*request)->Perform();                if ((*request)->SelfClose())              {                  s_download_lock.Lock();                  HttpHelper::s_async_downloads.remove(*request);                  s_download_lock.UnLock();              }            }    #ifdef _WIN32          return 1;  #else          return NULL;  #endif      }        static size_t write_callback(char *ptr, size_t size, size_t nmemb, void *userdata)      {          HttpDownloader::DownloadHelper::ThreadChunk* thread_chunk = reinterpret_cast<HttpDownloader::DownloadHelper::ThreadChunk*>(userdata);            if (thread_chunk->_download->m_is_cancel)          {              return 0;          }            thread_chunk->_download->m_httplock->Lock();          size_t written = 0;          if (thread_chunk->_startidx <= thread_chunk->_endidx)          {              int real_size = size * nmemb;              if (thread_chunk->_startidx + real_size > thread_chunk->_endidx)              {                  real_size = thread_chunk->_endidx - thread_chunk->_startidx + 1;              }                if (fseek(thread_chunk->_fp, thread_chunk->_startidx, SEEK_SET) != 0)              {                  perror("fseek");              }              else              {                  written = fwrite(ptr, 1, real_size, thread_chunk->_fp);                  thread_chunk->_startidx += written;              }              thread_chunk->_download->m_downloaded_size += written;          }          thread_chunk->_download->m_httplock->UnLock();            return written;      }        static int progress_callback(void *clientp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow)      {          HttpDownloader::DownloadHelper::ThreadChunk* thread_chunk = reinterpret_cast<HttpDownloader::DownloadHelper::ThreadChunk*>(clientp);            thread_chunk->_download->m_httplock->Lock();            double total_size = thread_chunk->_download->m_total_size;          double downloaded_size = thread_chunk->_download->m_downloaded_size;          void* userdata = thread_chunk->_download->m_userdata;          int callback_result = thread_chunk->_download->m_download_callback(total_size, downloaded_size, userdata);            thread_chunk->_download->m_httplock->UnLock();            return callback_result;      }    #ifdef _WIN32      static DWORD WINAPI DownloadWork(LPVOID param)  #else      static void* DownloadWork(void* param)  #endif      {          HttpDownloader::DownloadHelper::ThreadChunk* thread_chunk = reinterpret_cast<HttpDownloader::DownloadHelper::ThreadChunk*>(param);    #ifdef _WIN32          return thread_chunk->_download->DoDownload(thread_chunk);  #else          return (void *)(thread_chunk->_download->DoDownload(thread_chunk));  #endif      }  };    std::list< std::shared_ptr<HttpRequest::RequestHelper> > HttpHelper::s_async_requests;  std::list< std::shared_ptr<HttpDownloader::DownloadHelper> > HttpHelper::s_async_downloads;  int HttpHelper::s_id = 0;  HttpLock HttpHelper::s_request_lock;  HttpLock HttpHelper::s_download_lock;  CURLSH* HttpHelper::s_share_handle = nullptr;    HttpRequest::HttpRequest()      : m_request_handle(new HttpRequest::RequestHelper)  {      HttpHelper::Instance();  }    HttpRequest::~HttpRequest()  {  }    int HttpRequest::SetRetryTimes(int retry_times)  {      if (m_request_handle)      {          m_request_handle->SetRetryTimes(retry_times);          return REQUEST_OK;      }        return REQUEST_INIT_ERROR;  }    int HttpRequest::SetRequestId(int id)  {      if (m_request_handle)      {          m_request_handle->m_id = id;          return REQUEST_OK;      }        return REQUEST_INIT_ERROR;  }    int HttpRequest::SetRequestTimeout(long time_out)  {      if (m_request_handle)      {          if (m_request_handle->SetRequestTimeout(time_out) == CURLE_OK)          {              return REQUEST_OK;          }          else          {              return REQUEST_INVALID_OPT;          }      }        return REQUEST_INIT_ERROR;  }    int HttpRequest::SetRequestUrl(const std::string& url)  {      if (m_request_handle)      {          if (m_request_handle->SetRequestUrl(url) == CURLE_OK)          {              return REQUEST_OK;          }          else          {              return REQUEST_INVALID_OPT;          }      }        return REQUEST_INIT_ERROR;  }    int HttpRequest::SetMovedUrl(bool get_moved_url)  {      if (m_request_handle)      {          if (m_request_handle->SetMovedUrl(get_moved_url) == CURLE_OK)          {              return REQUEST_OK;          }          else          {              return REQUEST_INVALID_OPT;          }      }        return REQUEST_INIT_ERROR;  }    int HttpRequest::SetPostData(const std::string& message)  {      return SetPostData(message.c_str(), message.size());  }    int HttpRequest::SetPostData(const void* data, unsigned int size)  {      if (m_request_handle)      {          if (m_request_handle->SetPostData(data, size) == CURLE_OK)          {              return REQUEST_OK;          }          else          {              return REQUEST_INVALID_OPT;          }      }      return REQUEST_INIT_ERROR;  }    int HttpRequest::SetRequestHeader(std::map<std::string, std::string>& headers)  {      if (m_request_handle)      {          for (auto it = headers.begin(); it != headers.end(); ++it)          {              std::string header = it->first;              header += ": ";              header += it->second;              if (m_request_handle->SetRequestHeader(header) != CURLE_OK)              {                  return REQUEST_INVALID_OPT;              }          }          return REQUEST_OK;      }        return REQUEST_INIT_ERROR;  }    int HttpRequest::SetRequestHeader(const std::string& header)  {      if (m_request_handle)      {          if (m_request_handle->SetRequestHeader(header) == CURLE_OK)          {              return REQUEST_OK;          }          else          {              return REQUEST_INVALID_OPT;          }      }      return REQUEST_INIT_ERROR;  }    int HttpRequest::SetRequestProxy(const std::string& proxy, long proxy_port)  {      if (m_request_handle)      {          if (m_request_handle->SetRequestProxy(proxy, proxy_port) == CURLE_OK)          {              return REQUEST_OK;          }          else          {              return REQUEST_INVALID_OPT;          }      }        return REQUEST_INIT_ERROR;  }    int HttpRequest::SetResultCallback(ResultCallback rc)  {      if (m_request_handle)      {          m_request_handle->SetResultCallback(rc);          return REQUEST_OK;      }        return REQUEST_INIT_ERROR;  }    void HttpRequest::Close(HANDLE request_handle)  {      std::shared_ptr<RequestHelper>* request = (reinterpret_cast<std::shared_ptr<RequestHelper> *>(request_handle));      if (request == INVALID_HANDLE_VALUE || request == nullptr)      {          return;      }        bool basync = false;        HttpHelper::s_request_lock.Lock();      for (auto it = HttpHelper::s_async_requests.begin(); it != HttpHelper::s_async_requests.end(); ++it)      {          if ((*request) == *it)          {  #ifdef _WIN32              if (WaitForSingleObject((*request)->m_perform_thread, 10) == WAIT_OBJECT_0)  #else              if(pthread_kill((*request)->m_perform_thread, 0) != 0)  #endif              {                  HttpHelper::s_async_requests.remove(*request);              }              else              {                  (*request)->m_close_self = true;              }              basync = true;              break;          }      }      HttpHelper::s_request_lock.UnLock();        if (basync == false)      {          //request->reset();      }  }    HANDLE HttpRequest::PerformRequest(RequestType request_type)  {      if (m_request_handle)      {          if (m_request_handle->m_is_running)          {              return nullptr;          }            if (request_type == REQUEST_SYNC)          {              m_request_handle->Perform();                return &m_request_handle;          }          else if (request_type == REQUEST_ASYNC)          {              HttpHelper::s_request_lock.Lock();              HttpHelper::s_async_requests.push_back(m_request_handle);              std::shared_ptr<RequestHelper>& request = HttpHelper::s_async_requests.back();    #ifdef _WIN32              DWORD thread_id;              HANDLE async_thread = CreateThread(NULL, 0, HttpHelper::RequestThread, &request, 0, &thread_id);              request->m_perform_thread = async_thread;  #else              pthread_create(&(request->m_perform_thread), NULL, HttpHelper::RequestThread, &request);  #endif              HttpHelper::s_request_lock.UnLock();                return &request;          }            return nullptr;      }        return nullptr;  }    bool HttpRequest::GetHttpCode(HANDLE request_handle, long* http_code)  {      std::shared_ptr<RequestHelper>* request = reinterpret_cast<std::shared_ptr<RequestHelper>*>(request_handle);      if (request && http_code)      {          *http_code = (*request)->GetHttpCode();          return true;      }        return false;  }    bool HttpRequest::GetReceiveHeader(HANDLE request_handle, std::string* header)  {      std::shared_ptr<RequestHelper>* request = reinterpret_cast<std::shared_ptr<RequestHelper>*>(request_handle);      if (request)      {          return (*request)->GetHeader(header);      }        return false;  }    bool HttpRequest::GetReceiveContent(HANDLE request_handle, std::string* receive)  {      std::shared_ptr<RequestHelper>* request = reinterpret_cast<std::shared_ptr<RequestHelper>*>(request_handle);      if (request)      {          return (*request)->GetContent(receive);      }        return false;  }    bool HttpRequest::GetErrorString(HANDLE request_handle, std::string* error_string)  {      std::shared_ptr<RequestHelper>* request = reinterpret_cast<std::shared_ptr<RequestHelper>*>(request_handle);      if (request)      {          return (*request)->GetErrorString(error_string);      }        return false;  }    HttpRequest::RequestHelper::RequestHelper()      : m_curl_handle(nullptr)  #ifdef _WIN32      , m_perform_thread(nullptr)  #else      , m_perform_thread(-1)  #endif      , m_http_headers(nullptr)      , m_close_self(false)      , m_is_running(false)      , m_retry_times(HttpRequest::s_kRetryCount)      , m_http_code(0)      , m_post_data(nullptr)  {      m_result_callback = std::bind(&RequestHelper::ReqeustResultDefault, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);      m_id = HttpHelper::s_id;      m_curl_handle = curl_easy_init();      HttpHelper::set_share_handle(m_curl_handle);  }    HttpRequest::RequestHelper::~RequestHelper()  {      if (m_curl_handle)      {          curl_easy_cleanup(m_curl_handle);      }      if (m_http_headers)      {          curl_slist_free_all(reinterpret_cast<curl_slist*>(m_http_headers));      }      if (m_post_data)      {          delete m_post_data;          m_post_data = nullptr;      }  #ifdef _WIN32      if (m_perform_thread)      {          CloseHandle(m_perform_thread);      }  #endif  }    int HttpRequest::RequestHelper::SetRequestTimeout(long time_out)  {      if (m_curl_handle)      {          return curl_easy_setopt(m_curl_handle, CURLOPT_TIMEOUT, 0);      }        return CURLE_FAILED_INIT;  }    int HttpRequest::RequestHelper::SetRequestUrl(const std::string& url)  {      if (m_curl_handle)      {          if (url.substr(0, 5) == "https")          {              curl_easy_setopt(m_curl_handle, CURLOPT_SSL_VERIFYPEER, 0L);              curl_easy_setopt(m_curl_handle, CURLOPT_SSL_VERIFYHOST, 0L);          }          return curl_easy_setopt(m_curl_handle, CURLOPT_URL, url.c_str());      }        return CURLE_FAILED_INIT;  }    int HttpRequest::RequestHelper::SetMovedUrl(bool get_moved_url)  {      if (m_curl_handle)      {          if (get_moved_url)          {              curl_easy_setopt(m_curl_handle, CURLOPT_MAXREDIRS, 5);              return curl_easy_setopt(m_curl_handle, CURLOPT_FOLLOWLOCATION, 1L);          }          else          {              return curl_easy_setopt(m_curl_handle, CURLOPT_FOLLOWLOCATION, 0L);          }      }        return CURLE_FAILED_INIT;  }    int HttpRequest::RequestHelper::SetPostData(const void* data, unsigned int size)  {      if (m_curl_handle && data && size > 0)      {          CURLcode curl_code = curl_easy_setopt(m_curl_handle, CURLOPT_POST, 1);          if (curl_code == CURLE_OK)          {              if (m_post_data)              {                  delete m_post_data;                  m_post_data = nullptr;              }              m_post_data = new char[size];              memcpy(m_post_data, data, size);              curl_code = curl_easy_setopt(m_curl_handle, CURLOPT_POSTFIELDS, m_post_data);          }            if (curl_code == CURLE_OK)          {              curl_code = curl_easy_setopt(m_curl_handle, CURLOPT_POSTFIELDSIZE, size);          }            return curl_code;      }        return CURLE_FAILED_INIT;  }    int HttpRequest::RequestHelper::SetRequestHeader(const std::string& header)  {      if (m_curl_handle && header.empty() == false)      {          m_http_headers = curl_slist_append(reinterpret_cast<curl_slist*>(m_http_headers), header.c_str());            return m_http_headers ? CURLE_OK : CURLE_FAILED_INIT;      }        return CURLE_FAILED_INIT;  }    int HttpRequest::RequestHelper::SetRequestProxy(const std::string& proxy, long proxy_port)  {      //CURLOPT_PROXY      if (m_curl_handle)      {          CURLcode curl_code = curl_easy_setopt(m_curl_handle, CURLOPT_PROXYPORT, proxy_port);            curl_code = curl_easy_setopt(m_curl_handle, CURLOPT_PROXY, proxy.c_str());            return curl_code;      }        return CURLE_FAILED_INIT;  }    int HttpRequest::RequestHelper::SetResultCallback(ResultCallback rc)  {      m_result_callback = rc;        return CURLE_OK;  }    void HttpRequest::RequestHelper::ReqeustResultDefault(int id, bool success, const std::string& data)  {      //default request callback do nothing  }    int HttpRequest::RequestHelper::Perform()  {      if (m_curl_handle)      {          CURLcode curl_code;          if (m_http_headers)          {              curl_code = curl_easy_setopt(m_curl_handle, CURLOPT_HTTPHEADER, reinterpret_cast<curl_slist*>(m_http_headers));              if (curl_code != CURLE_OK)              {                  return curl_code;              }          }            m_is_running = true;          m_receive_header.clear();          m_receive_content.clear();            //set force http redirect          SetMovedUrl(true);            curl_code = curl_easy_setopt(m_curl_handle, CURLOPT_HEADERFUNCTION, HttpHelper::RetriveHeaderFunction);          curl_code = curl_easy_setopt(m_curl_handle, CURLOPT_HEADERDATA, &m_receive_header);            curl_code = curl_easy_setopt(m_curl_handle, CURLOPT_WRITEFUNCTION, HttpHelper::RetriveContentFunction);          curl_code = curl_easy_setopt(m_curl_handle, CURLOPT_WRITEDATA, &m_receive_content);            curl_code = curl_easy_setopt(m_curl_handle, CURLOPT_NOPROGRESS, 1);            curl_code = curl_easy_setopt(m_curl_handle, CURLOPT_NOSIGNAL, 1);          curl_code = curl_easy_setopt(m_curl_handle, CURLOPT_CONNECTTIMEOUT_MS, 0);            curl_code = curl_easy_perform(m_curl_handle);          if (curl_code == CURLE_OPERATION_TIMEDOUT)          {              int retry_count = m_retry_times;              while (retry_count > 0)              {                  curl_code = curl_easy_perform(m_curl_handle);                  if (curl_code != CURLE_OPERATION_TIMEDOUT) break;                  retry_count--;              }          }            curl_easy_getinfo(m_curl_handle, CURLINFO_RESPONSE_CODE, &m_http_code);          if (curl_code == CURLE_OK && m_http_code == 200)          {              m_result_callback(m_id, true, m_receive_content);          }          else          {              const char* err_string = curl_easy_strerror(curl_code);              m_error_string = err_string;              curl_code = CURLE_HTTP_POST_ERROR;              m_result_callback(m_id, false, m_receive_content);          }            m_is_running = false;            if (m_http_headers)          {              curl_slist_free_all(reinterpret_cast<curl_slist*>(m_http_headers));              m_http_headers = nullptr;          }            return curl_code;      }        return CURLE_FAILED_INIT;  }    bool HttpRequest::RequestHelper::GetHeader(std::string* header)  {      if (m_receive_header.empty()) return false;      else if (header) *header = m_receive_header;        return true;  }    bool HttpRequest::RequestHelper::GetContent(std::string* receive)  {      if (m_receive_content.empty()) return false;      else if (receive) *receive = m_receive_content;        return true;  }    bool HttpRequest::RequestHelper::GetErrorString(std::string* error_string)  {      if (m_error_string.empty()) return false;      else if (error_string) *error_string = m_error_string;        return true;  }    HttpDownloader::HttpDownloader()      :m_request_handle(new HttpDownloader::DownloadHelper)  {      HttpHelper::Instance();  }    HttpDownloader::~HttpDownloader()  {    }    int HttpDownloader::SetRequestProxy(const std::string& proxy, long proxy_port)  {      if (m_request_handle)      {          if (m_request_handle->SetRequestProxy(proxy, proxy_port) == CURLE_OK)          {              return 0;          }          else          {              return HttpRequest::REQUEST_INVALID_OPT;          }      }        return HttpRequest::REQUEST_INIT_ERROR;  }    int HttpDownloader::SetRetryTimes(int retry_times /* = s_kRetryCount */)  {      if (m_request_handle)      {          m_request_handle->SetRetryTimes(retry_times);          return HttpRequest::REQUEST_OK;      }        return HttpRequest::REQUEST_INIT_ERROR;  }    int HttpDownloader::SetTimeout(long time_out /* = 0 */)  {      if (m_request_handle)      {          if (m_request_handle->SetTimeout(time_out) == CURLE_OK)          {              return HttpRequest::REQUEST_OK;          }          else          {              return HttpRequest::REQUEST_INVALID_OPT;          }      }        return HttpRequest::REQUEST_INIT_ERROR;  }    int HttpDownloader::SetDownloadUrl(const std::string& url)  {      if (m_request_handle)      {          if (m_request_handle->SetRequestUrl(url) == CURLE_OK)          {              return HttpRequest::REQUEST_OK;          }          else          {              return HttpRequest::REQUEST_INVALID_OPT;          }      }        return HttpRequest::REQUEST_INIT_ERROR;  }    int HttpDownloader::SetUserData(void* userdata)  {      if (m_request_handle)      {          m_request_handle->SetUserData(userdata);            return HttpRequest::REQUEST_OK;      }      return HttpRequest::REQUEST_INIT_ERROR;  }    int HttpDownloader::SetRequestId(int id)  {      if (m_request_handle)      {          m_request_handle->SetRequestId(id);          return HttpRequest::REQUEST_OK;      }        return HttpRequest::REQUEST_INIT_ERROR;  }    int HttpDownloader::SetProgressCallback(ProgressCallback pc)  {      if (m_request_handle)      {          m_request_handle->SetProgressCallback(pc);            return HttpRequest::REQUEST_OK;      }        return HttpRequest::REQUEST_INIT_ERROR;  }    int HttpDownloader::SetResultCallback(ResultCallback rc)  {      if (m_request_handle)      {          m_request_handle->SetResultCallback(rc);            return HttpRequest::REQUEST_OK;      }        return HttpRequest::REQUEST_INIT_ERROR;  }    int HttpDownloader::DownloadFile(const std::string& file_name, int thread_count /* = 5 */)  {      if (m_request_handle)      {          m_request_handle->SetDownloadFile(file_name);          m_request_handle->SetDownloadThreadCount(thread_count);      }        return HttpRequest::REQUEST_INIT_ERROR;  }    HANDLE HttpDownloader::StartDownload(DownType down_type)  {      if (m_request_handle)      {          if (m_request_handle->m_is_running)          {              return nullptr;          }            if (down_type == DOWN_SYNC)          {              m_request_handle->Perform();                return &m_request_handle;          }          else if (down_type == DOWN_ASYNC)          {              HttpHelper::s_download_lock.Lock();              HttpHelper::s_async_downloads.push_back(m_request_handle);              std::shared_ptr<DownloadHelper>& request = HttpHelper::s_async_downloads.back();    #ifdef _WIN32              DWORD thread_id;              HANDLE async_thread = CreateThread(NULL, 0, HttpHelper::DownloadThread, &request, 0, &thread_id);              request->m_perform_thread = async_thread;  #else              pthread_create(&(request->m_perform_thread), NULL, HttpHelper::DownloadThread, &request);  #endif              HttpHelper::s_download_lock.Lock();                return &request;          }            return nullptr;      }        return nullptr;  }    void HttpDownloader::Close(HANDLE handle)  {      std::shared_ptr<DownloadHelper>* request = (reinterpret_cast<std::shared_ptr<DownloadHelper> *>(handle));      if (request == INVALID_HANDLE_VALUE || request == nullptr)      {          return;      }        bool basync = false;        HttpHelper::s_download_lock.Lock();      for (auto it = HttpHelper::s_async_downloads.begin(); it != HttpHelper::s_async_downloads.end(); ++it)      {          if ((*request) == *it)          {  #ifdef _WIN32              if (WaitForSingleObject((*request)->m_perform_thread, 10) == WAIT_OBJECT_0)  #else              if(pthread_kill((*request)->m_perform_thread, 0) != 0)  #endif              {                  HttpHelper::s_async_downloads.remove(*request);              }              else              {                  (*request)->m_close_self = true;              }              basync = true;              break;          }      }      HttpHelper::s_download_lock.UnLock();        if (basync == false)      {          (*request)->m_is_cancel = true;          //request->reset();      }  }    bool HttpDownloader::CancelDownload(HANDLE handle)  {      std::shared_ptr<DownloadHelper>* request = (reinterpret_cast<std::shared_ptr<DownloadHelper> *>(handle));      if (request == INVALID_HANDLE_VALUE || request == nullptr)      {          return false;      }        (*request)->m_is_cancel = true;        return true;  }    bool HttpDownloader::GetHttpCode(HANDLE handle, long* http_code)  {      std::shared_ptr<DownloadHelper>* request = reinterpret_cast<std::shared_ptr<DownloadHelper>*>(handle);      if (request && http_code)      {          *http_code = (*request)->GetHttpCode();          return true;      }        return false;  }    bool HttpDownloader::GetErrorString(HANDLE handle, std::string* error_string)  {      std::shared_ptr<DownloadHelper>* request = reinterpret_cast<std::shared_ptr<DownloadHelper>*>(handle);      if (request)      {          return (*request)->GetErrorString(error_string);      }        return false;  }    bool HttpDownloader::GetReceiveHeader(HANDLE handle, std::string* header)  {      std::shared_ptr<DownloadHelper>* request = reinterpret_cast<std::shared_ptr<DownloadHelper>*>(handle);      if (request)      {          return (*request)->GetHeader(header);      }        return false;  }    void* HttpDownloader::GetUserData(HANDLE handle)  {        std::shared_ptr<DownloadHelper>* request = reinterpret_cast<std::shared_ptr<DownloadHelper>*>(handle);      if (request)      {          return (*request)->GetUserData();      }        return nullptr;  }    HttpDownloader::DownloadHelper::DownloadHelper()  #ifdef _WIN32      : m_perform_thread(nullptr)  #else      : m_perform_thread(-1)  #endif      , m_close_self(false)      , m_retry_times(HttpDownloader::s_kRetryCount)      , m_thread_count(HttpDownloader::s_kThreadCount)      , m_http_code(0)      , m_time_out(0)      , m_proxy_port(0)      , m_total_size(0.0)      , m_downloaded_size(0.0)      , m_multi_download(false)      , m_download_fail(true)      , m_is_running(false)      , m_httplock(new HttpLock)      , m_userdata(NULL)  {      m_download_callback = std::bind(&DownloadHelper::DownloadDefaultCallback, this,          std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);      m_result_callback = std::bind(&DownloadHelper::ResultDefaultCallback, this,          std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);      m_id = HttpHelper::s_id;  }    HttpDownloader::DownloadHelper::~DownloadHelper()  {      if (m_perform_thread)      {  #ifdef _WIN32          CloseHandle(m_perform_thread);          m_perform_thread = nullptr;  #endif      }  }    int HttpDownloader::DownloadHelper::SetTimeout(long time_out /* = 0 */)  {      m_time_out = time_out;        return CURLE_OK;  }    int HttpDownloader::DownloadHelper::SetRequestUrl(const std::string& url)  {      m_url = url;        return CURLE_OK;  }    int HttpDownloader::DownloadHelper::SetRequestProxy(const std::string& proxy, long proxy_port)  {      m_http_proxy = proxy;      m_proxy_port = proxy_port;        return CURLE_OK;  }    int HttpDownloader::DownloadHelper::SetProgressCallback(ProgressCallback pc)  {      m_download_callback = pc;        return CURLE_OK;  }    int HttpDownloader::DownloadHelper::SetResultCallback(ResultCallback rc)  {      m_result_callback = rc;        return CURLE_OK;  }    int HttpDownloader::DownloadHelper::SetDownloadFile(const std::string& file_name)  {      m_file_path = file_name;        return CURLE_OK;  }    int HttpDownloader::DownloadHelper::SetDownloadThreadCount(int thread_count)  {      m_thread_count = thread_count;        return CURLE_OK;  }    int HttpDownloader::DownloadHelper::Perform()  {      m_total_size = GetDownloadFileSize();      if (m_total_size < 0)      {          return HttpRequest::REQUEST_PERFORM_ERROR;      }        std::string out_file_name = m_file_path;      std::string src_file_name = out_file_name;      out_file_name += ".dl";        FILE *fp = nullptr;  #ifdef _WIN32      fopen_s(&fp, out_file_name.c_str(), "wb");  #else      fp = fopen(out_file_name.c_str(), "wb");  #endif      if (!fp)      {          return HttpRequest::REQUEST_OPENFILE_ERROR;      }        //reset enviroment      m_downloaded_size = 0.0;      m_download_fail = false;      m_is_running = true;      m_is_cancel = false;        int down_code = HttpRequest::REQUEST_PERFORM_ERROR;      int thread_count = SplitDownloadCount(m_total_size);        m_thread_count = thread_count > m_thread_count ? m_thread_count : thread_count;      //文件大小有分开下载的必要并且服务器支持多线程下载时,启用多线程下载      if (m_multi_download && m_thread_count > 1)      {          long gap = static_cast<long>(m_total_size) / m_thread_count;  #ifdef _WIN32          std::vector<HANDLE> threads;  #else          std::vector<pthread_t> threads;  #endif            for (int i = 0; i < m_thread_count; i++)          {              ThreadChunk* thread_chunk = new ThreadChunk;              thread_chunk->_fp = fp;              thread_chunk->_download = this;                if (i < m_thread_count - 1)              {                  thread_chunk->_startidx = i * gap;                  thread_chunk->_endidx = thread_chunk->_startidx + gap - 1;              }              else              {                  thread_chunk->_startidx = i * gap;                  thread_chunk->_endidx = static_cast<long>(m_total_size)-1;              }    #ifdef _WIN32              DWORD thread_id;              HANDLE hThread = CreateThread(NULL, 0, HttpHelper::DownloadWork, thread_chunk, 0, &(thread_id));  #else              pthread_t hThread;              pthread_create(&hThread, NULL, HttpHelper::DownloadWork, thread_chunk);  #endif              threads.push_back(hThread);          }    #ifdef _WIN32          WaitForMultipleObjects(threads.size(), &threads[0], TRUE, INFINITE);          for (HANDLE handle : threads)          {              CloseHandle(handle);          }  #else          for(pthread_t thread : threads)          {              pthread_join(thread, NULL);          }  #endif      }      else      {          ThreadChunk* thread_chunk = new ThreadChunk;          thread_chunk->_fp = fp;          thread_chunk->_download = this;          thread_chunk->_startidx = 0;          thread_chunk->_endidx = static_cast<long>(m_total_size)-1;          down_code = DoDownload(thread_chunk);      }        if (m_download_fail == false)      {          fclose(fp);  #ifdef _WIN32          MoveFileExA(out_file_name.c_str(), src_file_name.c_str(), MOVEFILE_REPLACE_EXISTING);  #else          unlink(src_file_name.c_str());          rename(out_file_name.c_str(), src_file_name.c_str());  #endif      }      else      {  #ifdef _WIN32          DeleteFileA(out_file_name.c_str());  #else          unlink(out_file_name.c_str());  #endif      }        m_result_callback(m_id, m_download_fail ? false : true, "");        m_is_running = false;        return down_code;  }    bool HttpDownloader::DownloadHelper::GetHeader(std::string* header)  {      if (m_receive_header.empty()) return false;      else if (header) *header = m_receive_header;        return true;  }    bool HttpDownloader::DownloadHelper::GetErrorString(std::string* error_string)  {      if (m_error_string.empty()) return false;      else if (error_string) *error_string = m_error_string;        return true;  }    int HttpDownloader::DownloadHelper::DownloadDefaultCallback(double total_size, double downloaded_size, void* userdata)  {      return static_cast<int>(downloaded_size * 100 / total_size);  }    void HttpDownloader::DownloadHelper::ResultDefaultCallback(int id, bool success, const std::string& data)  {  }    double HttpDownloader::DownloadHelper::GetDownloadFileSize()  {      if (m_url.empty())      {          return -1.0;      }      else      {          double down_file_length = -1.0;          CURL *handle = curl_easy_init();          HttpHelper::set_share_handle(handle);            if (handle)          {              curl_easy_setopt(handle, CURLOPT_URL, m_url.c_str());              curl_easy_setopt(handle, CURLOPT_HEADER, 1);              curl_easy_setopt(handle, CURLOPT_NOBODY, 1);              curl_easy_setopt(handle, CURLOPT_FOLLOWLOCATION, 1);              curl_easy_setopt(handle, CURLOPT_MAXREDIRS, 5);              curl_easy_setopt(handle, CURLOPT_HEADERFUNCTION, HttpHelper::RetriveHeaderFunction);              curl_easy_setopt(handle, CURLOPT_HEADERDATA, &m_receive_header);              curl_easy_setopt(handle, CURLOPT_RANGE, "2-");                CURLcode curl_code = curl_easy_perform(handle);                if (curl_code == CURLE_OPERATION_TIMEDOUT)              {                  int retry_count = m_retry_times;                  while (retry_count > 0)                  {                      curl_code = curl_easy_perform(handle);                      if (curl_code != CURLE_OPERATION_TIMEDOUT) break;                      retry_count--;                  }              }                curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &m_http_code);                if (curl_code == CURLE_OK)              {                  curl_easy_getinfo(handle, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &down_file_length);                    //匹配"Content-Range: bytes 2-1449/26620" 则证明支持多线程下载                  std::regex pattern("CONTENT-RANGE\\s*:\\s*\\w+\\s*(\\d+)-(\\d*)/(\\d+)", std::regex::icase);                  m_multi_download = std::regex_search(m_receive_header, pattern);              }              else              {                 const char* err_string = curl_easy_strerror(curl_code);                 m_error_string = err_string;              }                            curl_easy_cleanup(handle);          }            return down_file_length;      }  }    int HttpDownloader::DownloadHelper::DoDownload(ThreadChunk* thread_chunk)  {      CURL* curl_handle = curl_easy_init();      HttpHelper::set_share_handle(curl_handle);        if (thread_chunk->_download->m_url.substr(0, 5) == "https")      {          curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 0L);          curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYHOST, 0L);      }        curl_easy_setopt(curl_handle, CURLOPT_URL, thread_chunk->_download->m_url.c_str());        const char* user_agent = ("Mozilla/5.0 (Windows NT 5.1; rv:5.0) Gecko/20100101 Firefox/5.0");      curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, user_agent);        curl_easy_setopt(curl_handle, CURLOPT_MAXREDIRS, 5L);      curl_easy_setopt(curl_handle, CURLOPT_FOLLOWLOCATION, 1L);        curl_easy_setopt(curl_handle, CURLOPT_NOSIGNAL, 1L);      curl_easy_setopt(curl_handle, CURLOPT_POST, 0L);        curl_easy_setopt(curl_handle, CURLOPT_CONNECTTIMEOUT_MS, 0L);      curl_easy_setopt(curl_handle, CURLOPT_TIMEOUT, thread_chunk->_download->m_time_out);   //0 means block always        curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, HttpHelper::write_callback);      curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, thread_chunk);        curl_easy_setopt(curl_handle, CURLOPT_NOPROGRESS, 0L);      curl_easy_setopt(curl_handle, CURLOPT_XFERINFOFUNCTION, HttpHelper::progress_callback);      curl_easy_setopt(curl_handle, CURLOPT_XFERINFODATA, thread_chunk);        curl_easy_setopt(curl_handle, CURLOPT_LOW_SPEED_LIMIT, 1L);      curl_easy_setopt(curl_handle, CURLOPT_LOW_SPEED_TIME, 5L);        std::string down_range;      std::ostringstream ostr;      ostr << thread_chunk->_startidx << "-" << thread_chunk->_endidx;      down_range = ostr.str();      curl_easy_setopt(curl_handle, CURLOPT_RANGE, down_range.c_str());        CURLcode curl_code = curl_easy_perform(curl_handle);      if (curl_code == CURLE_OPERATION_TIMEDOUT)      {          int retry_count = m_retry_times;          while (retry_count > 0)          {              curl_code = curl_easy_perform(curl_handle);              if (curl_code != CURLE_OPERATION_TIMEDOUT) break;              retry_count--;          }      }        long http_code;      curl_easy_getinfo(curl_handle, CURLINFO_RESPONSE_CODE, &http_code);      if (curl_code == CURLE_OK && (http_code >= 200 && http_code <= 300))      {          m_http_code = http_code;      }      else      {          const char* err_string = curl_easy_strerror(curl_code);          m_error_string = err_string;          thread_chunk->_download->m_download_fail = true;          m_http_code = http_code;      }        curl_easy_cleanup(curl_handle);        delete thread_chunk;        return curl_code;  }    int HttpDownloader::DownloadHelper::SplitDownloadCount(double down_size)  {      const double size_2mb = 2.0 * 1024 * 1024;      const double size_10mb = 10.0 * 1024 * 1024;      const double size_50mb = 50.0 * 1024 * 1024;        if (down_size <= size_2mb)      {          return 1;      }      else if (down_size > size_2mb && down_size <= size_10mb)      {          return static_cast<int>(down_size / (size_2mb));      }      else if (down_size > size_10mb && down_size <= size_50mb)      {          return HttpDownloader::s_kThreadCount + 1;      }      else      {          int down_count = static_cast<int>(down_size / size_10mb);          return down_count > 10 ? 10 : down_count;      }        return 1;  }    HttpRequest.cpp

使用libcurl库
demo使用封装的库来模拟请求数据和下载文件。
例子很简单,直接看代码:
// http_request.cpp : 定义控制台应用程序的入口点。  //    #include "HttpRequest.h"    #include <iostream>  #include <string>  #include <fstream>  #include <functional>    class DownCallbackClass  {  public:          DownCallbackClass() :m_down_finished(false) {}          ~DownCallbackClass() {}  public:          void DownResultCallback(int id, bool success, const std::string& data)          {                  m_down_finished = true;          }          int down_callback(double total_size, double downloaded_size, void* userdata)          {                  long tmp = static_cast<long>(downloaded_size / total_size * 100);                  printf("\r下载进度%d", tmp);                  return 0;          }          bool IsDownFinished(void) { return m_down_finished;  }  private:          bool m_down_finished;  };    class MyResultClass  {  public:          MyResultClass() : m_request_finished(false) { }          ~MyResultClass() { }    public:          void MyRequestResultCallback(int id, bool success, const std::string& data)          {                  if (success)                  {                          std::ofstream outfile;                          outfile.open("baidu.html", std::ios_base::binary | std::ios_base::trunc);                          if (outfile.good()) outfile.write(data.c_str(), data.size());                  }                  m_request_finished = true;          }          bool IsRequestFinish(void) { return m_request_finished;  }  private:          bool m_request_finished;  };    int _tmain(int argc, _TCHAR* argv[])  {          MyResultClass mc;            HttpRequest request;          request.SetRequestUrl("http://www.baidu.com");          request.SetResultCallback(std::bind(&MyResultClass::MyRequestResultCallback, &mc, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));          request.SetRequestHeader("User-Agent:Mozilla/4.04[en](Win95;I;Nav)");            HANDLE hRequest = request.PerformRequest(HttpRequest::REQUEST_ASYNC);          if (hRequest)          {                  while (mc.IsRequestFinish() == false) Sleep(300);                  long http_code;                  if (request.GetHttpCode(hRequest, &http_code))                          std::cout << "http code: " << http_code << std::endl;                    std::string header;                  if (request.GetReceiveHeader(hRequest, &header))                  {                          std::cout << header << std::endl;                  }                    HttpRequest::Close(hRequest);          }            HttpDownloader download;          DownCallbackClass dc;          const char* down_url = "http://dlsw.baidu.com/sw-search-sp/soft/71/10998/OfflineBaiduPlayer_151_V4.1.2.263.1432003947.exe";          const char* down_file = "BaiduPlayer.exe";            download.SetDownloadUrl(down_url);          download.SetProgressCallback(std::bind(&DownCallbackClass::down_callback, &dc, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));          download.SetResultCallback(std::bind(&DownCallbackClass::DownResultCallback, &dc, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));          download.DownloadFile(down_file);          HANDLE hDownload = download.StartDownload(HttpDownloader::DOWN_ASYNC);          if (hDownload)          {                  while (dc.IsDownFinished() == false)                  {                          Sleep(300);                  }                  //to do download finish clean up                  HttpDownloader::Close(hDownload);          }            return 0;  }

来自:http://www.cnblogs.com/jojodru/p/4551201.html

 本文由用户 rbyt 自行上传分享,仅供网友学习交流。所有权归原作者,若您的权利被侵害,请联系管理员。
 转载本站原创文章,请注明出处,并保留原始链接、图片水印。
 本站是一个以用户分享为主的开源技术平台,欢迎各类分享!
 本文地址:https://www.open-open.com/lib/view/open1433472489807.html
libcurl 网络工具包