一点一滴成长

导航

使用libcurl/curlpp进行http通信

1、使用libcurl进行http通信

  进入下载页面 https://curl.se/download.html 选择对应的版本,这里选择Windows 32-bit,下载完成后解压,include为头文件目录,bin目录下即为libcur的dll文件(.lib可以通过.def文件来生成),如下所示。如下代码为使用libcurl执行post请求的示例:

#include <iostream>
#include <curl/curl.h>

typedef struct {
    std::string body;
    size_t bodySize;
}stResponse;

size_t req_reply(void* ptr, size_t size, size_t nmemb, void* param)
{
    if (param == NULL || ptr == NULL || size == 0)
        return 0;

    size_t realsize = size * nmemb;
    stResponse* pResponse = (stResponse*)param;
    pResponse->body.append((char*)ptr, realsize);
    pResponse->bodySize = realsize;

    return realsize;
}

typedef struct {
    std::string header;
    size_t headerSize;
} stResponseHeader;

size_t responseHeaderCallback(void* ptr, size_t size, size_t nmemb, void* param) {
    if (param == NULL || ptr == NULL || size == 0)
        return 0;

    size_t realsize = size * nmemb;
    stResponseHeader* pResponseHeader = (stResponseHeader*)param;
    pResponseHeader->header.append((char*)ptr, realsize);
    pResponseHeader->headerSize = realsize;
    return realsize;
}

int main()
{
    CURL* curl;
    CURLcode res;

    curl_global_init(CURL_GLOBAL_ALL);

    curl = curl_easy_init();
    if (curl) {
        curl_easy_setopt(curl, CURLOPT_URL, "http://localhost:8080/update"); //请求地址
        //curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 10L); //10秒连接超时,包括DNS解析时间(libcurl使用操作系统中DNS服务地址)
        //curl_easy_setopt(curl, CURLOPT_DNS_CACHE_TIMEOUT, 10L); // DNS解析结果在缓存中的有效期,默认120秒
        curl_easy_setopt(curl, CURLOPT_TIMEOUT, 15L); //总超时时间(连接+NDS解析+等待数据),默认为0无超时,即一直等待数据(curl_easy_perform()一直阻塞等待数据)
        //curl_easy_setopt(curl, CURLOPT_POST, 1L); //设置请求方式为POST,不设置或设置为0则为GET请求,设置了CURLOPT_POSTFIELDS的话不再需要设置该参数
        curl_easy_setopt(curl, CURLOPT_POSTFIELDS, "{\"req\":\"hello\"}"); //设置POST请求体(参数),get请求的话,参数直接放到URL中,不用单独设置
        curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, strlen("{\"req\":\"hello\"}")); //设置POST请求体大小,如果请求体是字符串的话可以省略该设置,但建议显示设置改参数
        curl_slist* headerList = NULL;
        headerList = curl_slist_append(headerList, "Content-Type: application/json");
        curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headerList); //设置请求头
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, req_reply); //回调(实体数据)
        stResponse responseBody;
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void*)&responseBody); //设置req_reply回调中param参数
        curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, responseHeaderCallback); //回调(响应头)
        stResponseHeader responseHeader;
        curl_easy_setopt(curl, CURLOPT_HEADERDATA, (void*)&responseHeader); //设置responseHeaderCallback回调中param参数

        res = curl_easy_perform(curl); //执行请求
        if (res == CURLE_OK) {
            int responseCode = 0;
            curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &responseCode);
            std::cout << "code : " << responseCode << std::endl; //状态码
            std::cout << "response header : " << responseHeader.header.c_str() << std::endl; //响应头
            std::cout << "response body : " << responseBody.body.c_str() << std::endl; //响应体

        }
        else {
            fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
        }

        curl_slist_free_all(headerList);
        curl_easy_cleanup(curl);
    }

    curl_global_cleanup();
}

2、多线程与libcurl

  libcurl宣称是线程安全的,即可以创建多个线程,每个线程里创建一个CURL对象(禁止多个线程共享一个CURL对象)。但多线程下使用libcurl仍然会有两个问题:

  ①、curl_global_init()并不是线程安全的,所以不应该在每个线程里调用curl_global_init(),应当在程序初始化即主线程中执行curl_global_init(),程序结束之前调用curl_global_cleanup()。

  ②、对于linux系统,多线程中进行libcurl操作可能会出现问题,因为libcurl中域名解析中的超时机制通过信号实现(非线程安全),可以通过设置CURLOPT_NOSIGNAL选项为1来解决这个问题。设置CURLOPT_NOSIGNAL后DNS解析就没了超时检查,可以通过使用异步DNS(需重新编译libcurl)或者线程化DNS解析器(CentOS 7等默认启用该特性)来解决这个问题。

3、zlib库的编译

  使用curl进行Https通信的话,需要zlib和openssl。进入zlib官网 https://zlib.net 下载源码包,解压缩后打开CMake来生成zilb库项目:在source code输入栏选择zlib源码包解压后的目录,build目录输入栏选择项目生成所在的目录,点击configure来设置生成项目的类型以及平台,这里我们选择VS 15 (2017),32位,然后点击generate来生成项目,生成完成后,点击Open Project,打开项目。打开项目后可以看到项目里有很多工程,包括example、zlib、zlibstatic等,我们选择zlibstatic静态库项目,然后就可以编译生成zlib的静态库.lib文件了,编译完成后在生成目录里找到zlibstatic.lib文件(可以将其改名为zlib.lib),zlib源码目录里找到zlib.h,在项目目录里找到zconf.h,然后就可以使用了。

4、openssl

  编译openssl需要使用perl,所以先下载perl执行环境ActiveState或者Strawberry,这里选择Strawberry。选择zip包类型下载,解压后将其中的perl\bin目录添加到PATH环境变量中。

  去 https://openssl-library.org/source/ 下载openssl源码,这里选择1.1.1版本,下载完成后解压。打开x86 Native Tools Command Prompt for VS 2019命令行,cd进入openssl源码目录,执行perl Configure VC-WIN32 --release no-asm no-shared --prefix="D:\CPP_INCLUDE\openssl\x86",其中no-asm表示禁用汇编以防止电脑上没有汇编工具而生成失败, no-shared表示生成静态库 prefix指定输出目录,要生成64位版本的话使用VC-WIN64A。执行完成后执行nmake命令进行编译,编译完成后执行nmake test,执行完成后执行nmake install来将编译生成的库导入到输出目录。进入输出目录,libssl.lib,libcrypto.lib是我们需要的静态库文件,可以使用命令openssl version查看编译好的OpenSSL的版本。

5、编译支持https的libcurl

  进入curl下载地址https://curl.se/download.html下载curl源码后解压,打开x86 Native Tools Command Prompt for VS 2019命令行,cd进入curl源码目录中的winbuild目录,执行 

nmake /f Makefile.vc mode=static VC=16 MACHINE=x86 DEBUG=no WITH_SSL=static WITH_ZLIB=static WITH_PREFIX=D:\CPP_INCLUDE\curl\x86\ SSL_PATH=D:\CPP_INCLUDE\openssl\x86\ ZLIB_PATH=D:\CPP_INCLUDE\zlib\x86\ ,编译动态库的话选择mode=dll,VC=16表示使用VS2019,2022的话是17,生成DEBUG版本的话DEBUG=yes,SSL库为动态库的话WITH_SSL=dll,WITH_PREFIX指定编译结果输出路径,必须以反斜杠结尾,SSL_PATH指定使用的openssl库路径。

  另一种编译命令:nmake / f Makefile.vc mode = dll VC = 15 WITH_DEVEL = F:\CURL\openssl_static_lib - 1.1.1h - x64 WITH_SSL = static ENABLE_SSPI = no ENABLE_IPV6 = no,这个命令会将编译结果放到curl源码目录中的builds目录里,其中WITH_DEVEL表示用到第三方开发包的目录,这里是OpenSSL静态库目录, ENABLE_SSPI=no表示禁用SSPI,ENABLE_IPV6=no表示禁用ipV6功能。

  至此编译完成后我们就得到了zlib.lib,libssl.lib,libcrypto.lib、libcurl.lib这四个库,然后就可以添加它们到项目里进行http通信了(除此之外,还需要添加网络通信需要的库:ws2_32.lib、wldap32.lib、crypt32.lib、Normaliz.lib)。如果编译的是动态库的话,如下所示,可以直接使用编译生成目录中的curl.exe来进行测试,比如命令行中执行 curl.exe https://www.baidu.com --insecure,其中insecure表示跳过证书验证,证书验证可以去CURL官网下载cacert.pem。

  

6、证书相关

  使用CURL_VERIFY_PEER参数来设置验证HTTPS请求对象的合法性,即用第三方证书机构颁发的CA数字证书来验证服务端返回的证书,默认值是1。CURL_VERIFY_HOST参数主要用于https请求时返回的证书是否与请求的域名相符合,避免被中间着篡改证书文件,默认值是2。参数CURLOPT_CAINFO用来设置证书,如下所示:

curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2L);
curl_easy_setopt(curl, CURLOPT_CAINFO, "/path/to/ca-bundle.crt"/*证书所在目录*/);

  如果不想要验证证书和HOST,进行如下设置:

curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);

  下面为在curpp中不验证证书:

//curlpp
request.setOpt(new curlpp::options::SslVerifyPeer(false));
request.setOpt(new curlpp::options::SslVerifyHost(false));

 

posted on 2025-07-04 16:58  整鬼专家  阅读(127)  评论(0)    收藏  举报