使用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));
浙公网安备 33010602011771号