简介:这是一份基于Visual Studio 2013的C++示例工程,面向需要在Windows下快速上手libcurl的开发者,演示如何集成libcurl完成HTTP/HTTPS及FTP等网络通信。工程内包含完整源码与工程配置,通过curl_easy_setopt、回调函数、curl_easy_perform等接口展示GET/POST请求、SSL安全连接、代理与重定向等核心用法,并覆盖初始化、设置URL、执行请求与清理的完整调用流程。压缩包共61个文件,以18个头文件、16个DLL、6个PDB调试文件为主,另含lib、exp、cmake等编译辅助文件,整体体积仅7.03MB,目录按inc/src/x64等模块划分,便于直接对照学习。目前已有579人学习下载。借助该工程可省去自行搭建libcurl环境的繁琐流程,通过阅读和运行示例,可直观理解libcurl的回调机制、错误码处理,以及VS2013项目配置中的DLL依赖管理方法,并将其迁移到实际项目中,是一份高效实用的入门参考。
1. 项目背景与目标:为什么要在VS2013里搞libcurl
先说清楚这是个什么东西。libcurl是一个跨平台的网络传输开源库,支持HTTP、HTTPS、FTP、SMTP等一大堆协议,C/C++项目里用它做HTTP请求是最常见的做法之一。而VS2013(Visual Studio 2013)虽然是老物件了,但很多企业级项目、MFC桌面程序、工业控制软件至今还在用,尤其是那些维护多年的老代码库,不是想升级就能升的。
这个“VS2013调用libcurl示例工程”,本质上是解决一个很实际的问题:在老旧的开发环境里,如何把libcurl这个现代网络库用起来,让C++程序可以发HTTP请求、下载文件、调用Web API。核心价值就三个字——“能联网”。一个MFC桌面程序如果需要访问远程服务器、拉取数据、检查更新,libcurl基本是最省事的方案,比你自己封装WinINet或WinHTTP要省太多事。
适合看这篇东西的人,大概分三类。第一类是被老项目绑在VS2013上动弹不得的C++程序员,必须在这个环境下集成新功能。第二类是刚接触libcurl、想把网络功能塞进自己桌面程序里的新手,需要一份能跑通的示例代码。第三类是那种“库都下载好了但就是编译不过”的倒霉蛋,链接错误、找不到头文件、运行库不匹配——这些坑我全踩过,写出来给大家省点时间。
先说一下我的测试环境:Windows 7 x64,VS2013 Update 5,libcurl 7.71.1(源码包),代码写的是控制台工程,方便演示调用流程。MDI框架改动复杂,核心的libcurl调用逻辑是一样的。
2. 环境准备:VS2013安装和libcurl获取,一步都不能省
2.1 VS2013安装的几个关键点
VS2013的安装包现在不太好找,官网下载链接基本已经停了,一般是从MSDN I Tell You这样的镜像站拿。安装的时候有几个坑需要注意。
第一,Win10以上的系统装VS2013,容易卡在“正在安装”界面。这大多不是安装包问题,而是Windows Update服务没开或者.NET Framework版本不兼容。建议安装前先打开控制面板把“.NET Framework 3.5”勾上(包括2.0和3.0子项),然后跑一遍Windows Update,把系统补丁打全,再装VS2013,成功率会高很多。
第二,安装过程必须以管理员身份运行,不然有些组件写不进去。VS2013默认安装路径是C盘,如果C盘空间紧张,可以自定义到别的盘,但要注意后续可能涉及的路径配置问题。
第三,关于激活密钥——网上传的什么“永久激活密钥”基本都是假的,或者早就被封了。VS2013的社区版是免费的,专业版和企业版需要购买许可证。我的建议是:能用社区版就用社区版,功能足够;如果公司有正版授权,那就用公司的账号登录激活。别去折腾那些来路不明的密钥,装系统这种事,越规矩越省心。
提示:VS2013 Update 5一定要装。不装Update 5的话,对C++11的支持很差,后来编译libcurl源码时可能会遇到一些奇怪的语法报错。
2.2 libcurl的获取方式,两种都要讲清楚
libcurl的获取方式主要有两种,我在实际工程中都试过,各有优劣。
方式一:下载预编译的二进制包,从官方curl.se网站下载对应的Windows版本。好处是省事,头文件、库文件都是现成的,解压即用。坏处是版本更新不及时,而且预编译包默认用的是DLL运行库,你工程里如果设置了其他运行时库,可能对不上。
方式二:下载源码(比如7.71.1版本),自己编译。这是最稳妥的方式,因为你完全可以把控编译选项,比如静态链接还是动态链接、是否支持OpenSSL、是否启用特定协议等。我自己的工程就是这么干的,编译出来的libcurl跟我的程序完全匹配,后续部署时不用带着一堆DLL到处跑。
这里要注意,源码编译虽然灵活,但门槛确实高一些。你得先装好依赖库(OpenSSL、zlib等),然后用CMake生成VS2013的工程文件,再编译。一个细节:源码目录里有个winbuild文件夹,里面有nmake的编译脚本,可以用命令行编译,比CMake更简单直接。具体命令是这样:
cd curl-7.71.1/winbuild nmake /f Makefile.vc mode=dll VC=12 ENABLE_WINSSL=yesVC=12对应的就是VS2013的编译器版本(VC10是2010,VC11是2012,VC12就是2013)。ENABLE_WINSSL=yes表示用Windows自带的SSL库,这样就不用额外编译OpenSSL了。如果你的HTTPS请求涉及自签名证书、双向认证等特殊场景,需要额外配置,但普通场景下ENABLE_WINSSL已经够用。
编译结束后,在curl-7.71.1/build目录下会生成libcurl.dll、libcurl.lib、头文件include目录。把这些文件拷贝出来备用。
3. 工程配置:三个目录一处宏,全弄对就成功了一半
3.1 创建示例工程,选择对的项目类型
打开VS2013,新建项目,选“Win32控制台应用程序”,项目名称随便起,比如“CurlDemo”。在应用程序设置里,把“空项目”勾上。如果你以后要在MFC项目里用,原理完全一样,只是把库文件加到MFC工程里而已,调用代码不用改。
一个真实的经验:很多人图省事,直接用VS2013自带的“Windows服务”或“ATL项目”来跑测试代码,结果编译出来各种链接错误。建议就是老老实实建控制台工程,先把库调通了,再往正式项目里集成。
3.2 配置Include目录、Lib目录和附加依赖项
在解决方案资源管理器里右键项目,选“属性”,然后按照下面这个路径一步步配:
第一步,配置“C/C++” -> “常规” -> “附加包含目录”,把libcurl的include文件夹路径加进去。比如:
E:\third_party\curl-7.71.1\include这个目录下有curl文件夹,里面是curl.h、easy.h等头文件。代码里#include <curl/curl.h>能编过,靠的就是这个配置。
第二步,配置“链接器” -> “常规” -> “附加库目录”,把lib目录路径加进去:
E:\third_party\curl-7.71.1\build\lib第三步,配置“链接器” -> “输入” -> “附加依赖项”,填上:
libcurl.lib注意,如果你链接的是libcurl的dll版本,还需要保证运行时libcurl.dll在你的程序目录或者系统PATH里能找到。如果是静态链接,还需要额外加一些依赖库,这个下面单独说。
3.3 运行时库的选择,一个让人崩溃的坑
这是最容易出问题的地方。VS2013的“项目属性” -> “C/C++” -> “代码生成” -> “运行时库”,有四个选项:多线程调试(/MTd)、多线程(/MT)、多线程调试DLL(/MDd)、多线程DLL(/MD)。
libcurl的预编译版本默认是配合动态运行库(/MD)使用的。如果你在工程里选了静态运行库(/MT),编译时你可能会看到一堆_vsnprintf、_beginthread等符号无法解析的错误。
我当时是这么解决的:工程属性里把“配置”切到“Debug”,运行时库选“多线程调试(/MTd)”;配置切到“Release”,运行时库选“多线程(/MT)”。然后从源码编译libcurl的时候,也对应地选静态运行库。
注意静态链接libcurl,因为libcurl会依赖ssl、zlib等库,完整的附加依赖项表大概是:
libcurl.lib ws2_32.lib winmm.lib crypt32.lib wldap32.lib这些库缺一个,链接器就会报“无法解析的外部符号”。网上有各种说法,有的说还要加normaliz.lib,有的说不用。我的经验是,VS2013 + libcurl 7.71.1 + WinSSL模式,上面五个就够了。如果还报错,再逐个尝试加上。
4. 代码实现:一个完整的HTTP请求要走完这五步
4.1 初始化环境和全局清理
libcurl的使用套路很固定,就像一个“打开-使用-关闭”的过程。第一步是调用curl_global_init()初始化全局环境,这个函数会初始化SSL库、socket库等底层依赖。
#include <iostream> #include <string> #include <curl/curl.h> // 全局初始化只做一次,多次调用也没问题,但建议放在程序入口 curl_global_init(CURL_GLOBAL_DEFAULT); // ... 业务代码 ... // 程序退出时清理 curl_global_cleanup();CURL_GLOBAL_DEFAULT是默认值,它会把SSL和WinSocket都初始化好。也可以只初始化SSL:CURL_GLOBAL_SSL。但没必要卡那么细,直接用DEFAULT就行。注意,curl_global_init和curl_global_cleanup要成对出现,而且尽量在程序的开头和结尾各调用一次。不要在请求函数里反复调用,会影响性能。
4.2 数据回调函数的写法,很多新手在这里丢数据
libcurl请求返回的数据不是直接给你一个缓冲区,而是通过回调函数一块一块地交给你的。为什么这样设计?因为大文件下载时,数据是一块块到达的,回调机制让你随时处理,避免一次性占用大量内存。
回调函数的签名是固定的:
size_t WriteCallback(void* contents, size_t size, size_t nmemb, void* userp) { size_t totalSize = size * nmemb; // userp指向你传入的自定义数据结构,这里用string来存 std::string* str = static_cast<std::string*>(userp); str->append((char*)contents, totalSize); return totalSize; }关键点有两个。第一,必须返回实际处理的字节数,也就是size * nmemb。如果返回的和接收到的数量不一致,libcurl会认为传输出错。第二,userp参数可以传任意指针,在curl_easy_setopt里通过CURLOPT_WRITEDATA选项传入。这里传一个std::string*过去,数据就自动累积起来了。
经验:回调代码要尽量简洁,不要做耗时操作,比如写磁盘、打日志。因为回调运行在libcurl的内部线程上下文里,拖得越久,传输效率越低。一次下载大文件时,我在回调里直接解析JSON,结果下载速度被拖慢了将近一半。后来改成先把数据存内存,下载完了再统一解析。
4.3 构造请求并执行:easy接口的完整流程
这里演示一个最常见的GET请求,目标URL用https://www.baidu.com,验证HTTPS请求能不能通:
// 创建一个easy句柄 CURL* curl = curl_easy_init(); if (!curl) { std::cerr << "curl_easy_init failed" << std::endl; return -1; } // 设置请求URL curl_easy_setopt(curl, CURLOPT_URL, "https://www.baidu.com"); // 设置跟随重定向 curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); // 设置超时时间(秒) curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 10L); curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30L); // 设置SSL证书验证(默认就是开启的,这里显式声明) curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L); curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2L); // 设置回调函数和接收数据的容器 std::string response; curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response); // 执行请求 CURLcode res = curl_easy_perform(curl); if (res != CURLE_OK) { std::cerr << "curl_easy_perform failed: " << curl_easy_strerror(res) << std::endl; } else { long httpCode = 0; curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &httpCode); std::cout << "HTTP Status Code: " << httpCode << std::endl; std::cout << "Response size: " << response.size() << " bytes" << std::endl; std::cout << response << std::endl; // 谨慎输出大响应 } // 清理句柄 curl_easy_cleanup(curl);代码本身很直白,但我遇到过几个问题,值得多说几句。
第一,超时设置。CURLOPT_CONNECTTIMEOUT是连接超时,CURLOPT_TIMEOUT是总超时。如果你的程序要长时间下载大文件,TIMEOUT设太短会中途断掉;设太长,一旦服务端没有响应,界面就像卡死了一样。另一个实用方案是把CURLOPT_NOSIGNAL设置为1L,防止libcurl在DNS解析时因信号中断导致多线程崩溃。
第二,SSL验证。默认情况下CURLOPT_SSL_VERIFYPEER是开启的,验证服务器证书。但开发阶段如果用的是自签名HTTPS证书,经常验证失败。有两个选择:开发环境可以临时把验证关掉,正式环境必须开启,否则万一中间人攻击,数据就裸奔了。代码里可以加一个宏来控制:
#ifdef _DEBUG curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); // 调试时跳过证书验证 #else curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L); curl_easy_setopt(curl, CURLOPT_CAINFO, "cacert.pem"); // 正式环境指定CA证书 #endif第三,curl_easy_perform是阻塞式的。如果你的程序有UI界面,MFC里直接调用会导致界面卡住。解法是把请求放到工作线程里,或者用libcurl multi接口做异步。后面会简单提一下,这里先不展开。
4.4 带参数的POST请求,模拟表单提交
实际项目里,GET请求可能只占一半,另一半是POST。POST请求的两种常见方式:URL编码表单(application/x-www-form-urlencoded)和JSON体(application/json)。
表单提交:
// 构造提交数据 std::string postData = "username=admin&password=123456"; // 重新复用上面的curl句柄 curl_easy_setopt(curl, CURLOPT_URL, "http://192.168.1.100:8080/api/login"); curl_easy_setopt(curl, CURLOPT_POST, 1L); curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postData.c_str()); curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, postData.size());JSON提交:
std::string jsonData = R"({"name":"libcurl","type":"http"})"; curl_easy_setopt(curl, CURLOPT_URL, "http://192.168.1.100:8080/api/upload"); curl_easy_setopt(curl, CURLOPT_POST, 1L); curl_easy_setopt(curl, CURLOPT_POSTFIELDS, jsonData.c_str()); curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, jsonData.size()); // 设置Content-Type头 struct curl_slist* headers = NULL; headers = curl_slist_append(headers, "Content-Type: application/json"); headers = curl_slist_append(headers, "Accept: application/json"); curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); // 执行完后释放头列表 CURLcode res = curl_easy_perform(curl); curl_slist_free_all(headers);提交的数据如果有中文,需要URL编码,用curl_easy_escape函数处理。比如:
char* encoded = curl_easy_escape(curl, "张三", 0); std::string encodedStr = encoded; curl_free(encoded);这里有个隐藏坑:POSTFIELDSIZE的类型是long,数据量超过2GB(不常见但存在)就会出问题,更大数据建议用CURLOPT_POSTFIELDSIZE_LARGE。普通场景不需要在意。
4.5 文件下载和进度显示:调用场景的延伸
libcurl除了发请求,还经常用来下载文件。文件下载本质上就是回调函数不写string,而是写文件句柄:
FILE* fp = fopen("setup.exe", "wb"); curl_easy_setopt(curl, CURLOPT_URL, "http://example.com/setup.exe"); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, NULL); // 省略自定义回调 curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp); // 直接用libcurl内置的文件写入 // 设置进度回调 curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L); curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, ProgressCallback); curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, &progressData); CURLcode res = curl_easy_perform(curl); fclose(fp);进度回调的写法也有固定套路:
int ProgressCallback(void* clientp, double dltotal, double dlnow, double ultotal, double ulnow) { double percent = 0.0; if (dltotal > 0) { percent = (dlnow / dltotal) * 100.0; } // 这里可以更新UI进度条 // 返回非0值会中止传输 return 0; }这个函数的返回值很关键。如果你的业务逻辑需要在下载中途取消(比如用户点“取消”按钮),设置一个全局标志,然后在回调里检查标志并返回1,libcurl就会中止传输。
5. 编译链接常见错误:把这些“坑”都写进速查表
5.1 “无法打开文件 libcurl.lib”和头文件找不到
这类问题百分之百是路径配错了。VS2013的工程属性里面有两个配置:Debug和Release,你把include路径配在Debug里,Release编译时照样找不到。很多人只改了一个配置,另一个没改,就会遇到“明明配好了却还是报错”的情况。解决办法是:右键项目属性时,先在最上方“配置”下拉框里选“所有配置”,再统一设置路径,就不会漏了。
链接阶段提示“无法打开文件”还有一种可能:libcurl.lib是32位的,工程是64位的(或反过来)。这个看工程属性里的“平台”就知道,x86对应32位,x64对应64位。lib本身是有位数之分的,别混用。
5.2 “无法解析的外部符号 __imp_XXXX”
这个报错的本质是:你告诉编译器要链接libcurl.lib,但编译器在库里找不到对应的函数符号。原因有两类:
第一类,libcurl是静态库,你少加了依赖库。前面说了,静态链接还需要ws2_32.lib、winmm.lib、crypt32.lib、wldap32.lib。这四个缺一不可。VS2013里可以加一行#pragma comment(lib, "ws2_32.lib"),用代码控制链接,挺方便的。
第二类,运行库冲突。libcurl如果是用/MT编译的,你的工程用了/MD,符号解析就可能失败。看编译选项:
#pragma comment(lib, "ws2_32.lib") #pragma comment(lib, "winmm.lib") #pragma comment(lib, "crypt32.lib") #pragma comment(lib, "wldap32.lib")在代码前部加这些编译指令,比在项目属性里配“附加依赖项”更不容易忘。
第三类,libcurl库是用不同版本的MSVC编译的。比如预编译包可能是VS2015编译的,你拿来在VS2013链接,C运行时库的符号对不上。这种情况只能自己重新编译libcurl,用VC=12的makefile。
5.3 运行时报错“应用程序无法正常启动0xc000007b”
这个错误经典到让人头皮发麻。三个字总结就是“不匹配”——libcurl.dll的应用位数和你的exe位数不一致。比如exe是32位的,libcurl.dll是64位的,一跑就报这个。解决思路:确认exe的位数,在x86/x64的LLC里找到对应版本的libcurl.dll。如果还不行,用Dependency Walker(或自带的Dependencies工具)查一下dll的依赖项是否都齐全。
5.4 Debug版本正常、Release版本报错
出现这个,先看两个地方。一是运行时库设置,Debug默认/MTd,Release默认/MT,如果你改了其中一个没改另一个,就会出问题。二是条件编译宏,_DEBUG在Debug里自动定义,Release里没有。如果你在代码里加了#ifdef _DEBUG控制SSL验证之类的逻辑,别忘了检查。
6. 结合VS2013 MFC和OpenCV安装场景:这些问题你迟早会遇到
6.1 MFC程序如何接入libcurl
现在的桌面程序很多还是MFC框架,接入libcurl和Win32控制台工程没有本质区别,但有几个细节要注意。
MFC程序是事件驱动的,你把curl_easy_perform放在按钮的OnBnClicked事件里,点击按钮后界面会卡住,直到请求完成。解决办法是用工作线程:
UINT DownloadThread(LPVOID param) { // 执行libcurl请求 // 通过PostMessage通知主线程更新UI ::PostMessage(hWnd, WM_DOWNLOAD_FINISHED, 0, 0); return 0; } // 在按钮事件里启动线程 AfxBeginThread(DownloadThread, NULL);一个真实的例子:我在MFC工程里接入libcurl后,本来想把HTTP请求和进度条都放主窗口里跑,结果点击“下载”按钮后整个窗口无响应,进度条卡在0%。改成AfxBeginThread启动后台线程,用PostMessage把进度通知回主线程,界面才正常刷新。
如果用到MFC的调试new宏(DEBUG_NEW),在包含curl头文件的地方可能会报C4996警告,说“localtime安全”之类的。这是因为libcurl的头文件里有些函数在MFC环境中被认为不安全。处理方法是定义CURL_STATICLIB宏,或者把libcurl的头文件include放在MFC标准头文件之后。
6.2 OpenCV和libcurl一起用:给自动采集程序装上传功能
热词里出现了“opencv安装教程vs2013”,这确实是很多人折腾过的组合。OpenCV负责图像采集和识别,libcurl负责把结果上传服务器——这个场景在工业质检、车牌识别、安防监控里非常常见。
如果两个库要在同一个VS2013工程里共存,最容易出现的问题是“符号重复”或“命名空间冲突”。OpenCV是C++库,libcurl是纯C库(虽然提供了C++兼容头文件),两者一般不会冲突。需要注意的还是位数匹配问题:OpenCV和libcurl的库都必须是同一架构(都是32位或都是64位)。
代码层面,常用模式是:OpenCV把图像数据存在std::vector<uchar>里,然后libcurl通过回调函数搬运数据。有个技巧:如果你想POST上传一张图片,可以用CURLOPT_READFUNCTION配合自定义读取回调来发送cv::Mat的内容,这样就不用先存成临时文件再上传,效率高不少。
6.3 卸载不掉VS2013怎么办:一个真实翻车案例
热词里还有“vs2013卸载不掉”,这个我遇到过。有一次想换VS2015,卸载VS2013时卡在“正在卸载Microsoft Visual C++ 2013 Redistributable”,进度条纹丝不动。
试了一下午,最后用微软官方的Visual Studio Uninstaller工具(github上微软官方发布的小工具,搜“vs_installer”就能找到)清理了卸载残留,才勉强搞定。实际操作中,还真的可以试试“先修复再卸载”这个节奏,很多情况下修复能先把损坏的组件补回来,再走卸载流程反而更快。
注意:如果电脑上安装了Microsoft Visual C++ 2013 Redistributable (x64)和(x86)两个版本,卸载VS2013后不要顺手把Redistributable也全删了——有些老软件可能依赖它。我的建议是:保留Redistributable,只卸载IDE部分。
7. 性能优化与多线程:libcurl在实际工程里还能怎么玩
7.1 用multi接口做并发请求
curl_easy_perform是阻塞的,一次只能发一个请求。如果程序需要同时请求多个URL(比如批量查询、并发下载),用easy接口逐个请求,耗时是串行的,效率很糟。
libcurl的multi接口就是干这个的:它允许你同时维护多个easy句柄,在一个线程里非阻塞地调度它们。核心API是curl_multi_add_handle、curl_multi_perform、curl_multi_info_read。用multi接口,你甚至不用创建一堆线程,单个线程就能管理几十个并发请求。
我在一个自动批量检查链接有效性的工具里,就是用multi接口同时发50个请求,比单线程快将近50倍(虽然瓶颈在带宽和对方服务器限制上)。
7.2 连接复用和DNS缓存
libcurl默认会对同一个host复用连接,前提是你复用了同一个curl句柄,或者多个句柄共享同一个多接口。如果每次请求都创建新句柄、销毁旧句柄,每次都得重新TCP握手、TLS握手,那性能就白丢了一截。
合理做法:把curl句柄定义成局部变量,但在循环体外创建,循环内只改URL、POST数据,执行完不要清理句柄,留着复用。我见过一些半年经验的程序员在循环里频繁curl_easy_init和curl_easy_cleanup,每次请求都慢100毫秒以上。不用太在意,稍加调整就能把性能追回来。
DNS缓存方面,设置了CURLOPT_DNS_CACHE_TIMEOUT可以控制DNS解析结果缓存时间,默认60秒。如果访问的是内网IP地址或固定IP服务,可以考虑设大一些,比如300秒,减少重复DNS查询。
7.3 断点续传和限速
下载大文件时,支持断点续传是个很实用的功能:
// 从偏移量offset开始下载,假设之前已下载了1000字节 curl_easy_setopt(curl, CURLOPT_RESUME_FROM_LARGE, 1000L);上传文件也类似,用CURLOPT_INFILESIZE_LARGE告诉服务端文件大小。注意,断点续传需要服务端支持Range头,大部分现代Web服务器默认支持。
限速用CURLOPT_MAX_RECV_SPEED_LARGE和CURLOPT_MAX_SEND_SPEED_LARGE,单位是字节/秒。比如想限制下载速度不超过200KB/s:
curl_easy_setopt(curl, CURLOPT_MAX_RECV_SPEED_LARGE, 200 * 1024);这个功能在实际场景里很关键——特别是需要同时跑多个网络任务时,不限速会吃满带宽,影响其他业务。
8. 最后分享一个小经验:先跑通再说优化
我见过很多人在图省事和追求完美之间反复横跳,最后卡在第一步。做这个VS2013 + libcurl的示例工程,我的建议是:先让它跑起来,看到控制台输出了HTTP状态码和响应内容,然后你再慢慢去优化细节。
具体说,第一件事是把环境配置全部搞定,Debug和Release都能编译通过,这是最重要的一步。第二件事是定一个小目标,比如“成功请求一个API并输出返回的JSON字符串”,目标定得越小越容易达成。第三件事才考虑封装、多线程、性能优化这些东西。
再分享一个调试技巧:用libcurl经常遇到“请求发出去但服务端没收到”的情况。先用抓包工具确定请求有没有真正发出,如果没有,十有八九是链接配置问题;如果有但服务端报错,检查Content-Type、User-Agent等头部字段。我发现很多奇怪问题其实出在User-Agent上,有些服务端会拦截空UA的请求,直接设置一个常规UA能省掉不少麻烦:
curl_easy_setopt(curl, CURLOPT_USERAGENT, "Mozilla/5.0 (Windows NT 6.1; WOW64)");希望这些操作经验能帮你少走点弯路。VS2013虽然老,但搭配libcurl依然能干活,而且是很多老项目的救星。有问题欢迎交流,我尽量有空就回复。
本文还有配套的精品资源,点击获取