_虹_
2019-04-11 20:32:36
Libcurl是一个开源的客户端URL传输库,支持DICT,FILE,FTP,FTPS,Gopher,HTTP,HTTPS,IMAP,IMAPS,LDAP,LDAPS,POP3,POP3S,RTMP,RTSP,SCP,SFTP,SMB,SMBS,SMTP,SMTPS,Telnet和TFTP,支持SSL证书,HTTP POST,HTTP PUT,FTP上传,基于HTTP表单的上传,代理服务器,HTTP / 2,cookies,用户名+密码验证(Basic,Plain,Digest,CRAM-MD5,NTLM,Negotiate and Kerberos)传输简历,代理隧道等等。
然而这篇文章只会讲编译,HTTP GET,POST ,cookies,HTTPS和代理。
Curl的官网:https://curl.haxx.se/
Libcurl:https://curl.haxx.se/libcurl/
Libcurl官方教程:https://curl.haxx.se/libcurl/c/libcurl-tutorial.html
有啥用呢,(对于算法竞赛这东西显然没用),但是我们可以用它实现一些具有一定实用意义的小工具。
环境:VS2015+libcurl
在官网可以下载libcurl的最新版本。
解压文件,在projects\Windows下是libcurl对使用vs编译提供的个版本工程,可根据自己vs对应的vc版本进行选择。
打开对应版本文件夹下的curl-all.sln,选择解决方案配置和平台。(图中编译了64位debug版本动态库。)
点击生成,选择生成解决方案进行编译。
生成的文件在build目录下,如图的配置,生成文件位于build\Win64\VC14\DLL Debug - DLL Windows SSPI,分别是libcurld.lib,libcurld.dll。
支持https的libcurl的编译就完成了。交叉编译openssl从来没成功过。
使用vs2015建立个命令行应用的项目(不会请移步baidu)。
复制ibcurl中的include\curl目录和编译出的lib,dll到建立的工程目录下。
在vs中点击项目,选择属性,vc++目录,将curl文件夹添加至包含目录,lib,dll所在目录添加至库目录。
选择连接器,输入,添加libcurld.lib(或对应版本),保存设置。
然后再代码中include curl.h就可以开始使用libcurl了。
本文只介绍libcurl的easy接口,multi和shared接口请自学。
先介绍几个注意事项:
纯竞赛程序员听不懂?那忽略上面也没啥关系。有纯竞赛的同学会看到这里吗?
先上个最简单的示例程序:获取baidu首页并在屏幕上输出。
#include <iostream>
#include "curl/curl.h"
int main()
{
CURL* curl_handle;
curl_global_init(CURL_GLOBAL_ALL);
curl_handle = curl_easy_init();
if(!curl_handle)return -1;
curl_easy_setopt(curl_handle,CURLOPT_URL,"http://www.baidu.com");
CURLcode res = curl_easy_perform(curl_handle);
curl_easy_cleanup(curl_handle);
curl_global_cleanup();
return 0;
}
看不懂?我们来一句一句解释:
CURL* curl_handle;
保存一个curl的句柄,所有收发数据都要围绕这个句柄进行。
curl_global_init(CURL_GLOBAL_ALL);
对libcurl的全局进行加载,这个函数不是线程安全的。
curl_handle = curl_easy_init();
获取一个curl句柄(easy handle)。
当没有调用过curl_global_init()时,curl_easy_init();会自动对前者进行调用。但前者不是线程安全的,所以最好手动调用global init
if(!curl_handle)return -1;
如果返回NULL,说明创建新句柄失败。
curl_easy_setopt(curl_handle,CURLOPT_URL,"http://www.baidu.com");
设置访问的url,这个函数后面再进行详细解释。其实基本上啥功能的实现都和它有关。
CURLcode res = curl_easy_perform(curl_handle);
发送curl_handle的请求并获取请求返回的状态(注意不是获取到的数据)。
curl_easy_cleanup(curl_handle);
释放curl句柄。
curl_global_cleanup();
关闭libcurl库。
貌似这玩意看起来干不了啥。 高端操作后面当然会讲了。
还是先上示例代码:下载并保存页面及超时重试。
size_t download(char* ptr, size_t size, size_t num, string* stream)
{
if (stream == NULL)
return 0;
stream->append(ptr, size*num);
return size*num;
}
CURLcode DownloadPage(CURL* curl_handle, string url, string& data)
{
curl_easy_setopt(curl_handle, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl_handle, CURLOPT_HTTPGET, true);//设置请求方式为http get
curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, download);
curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, &data);
curl_easy_setopt(curl_handle, CURLOPT_FOLLOWLOCATION, true);//设置libcurl自动进行重定向
curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, false);//不验证ssl证书。
curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYHOST, false);//设置libcurl不验证host。忽略host验证必须同时忽略ssl证书验证。
curl_easy_setopt(curl_handle, CURLOPT_TIMEOUT, CURL_DOWNLOAD_TIMEOUT);
return curl_easy_perform(curl_handle);
}
bool AutoRestartDownload(CURL* curl_handle, string url, string& data)
{
CURLcode res = (CURLcode)0;
int cnt = 0;
do
{
data.clear();
res = DownloadPage(curl_handle, url, data);
++cnt;
} while (res == 28&&cnt<=MAX_DOWNLOAD_RETRY);
return (res == 0);
}
总代码有点太长,上一下主体部分吧。
part3的代码是把获取的页面输出到了屏幕,但是其实获取到的数据我们是可以选择处理方式的。如果我们需要处理获取的数据,那我们需要向libcurl注册一个回调函数。
curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, function_name);
这个属性用于注册回调函数。
回调函数的形式是:
size_t function_name(char* ptr, size_t size, size_t num, data_type* stream)
这个函数的返回值表示成功读取了多少数据,因此一般应返回size*num。
如果我们想让回调函数获得更多的信息,比如获取的数据应该保存在哪,这就要用到另一个属性: CURLOPT_WRITEDATA。
curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, buffer);
这个函数会设置用户数据的指针,也就是上面回调函数获取到的第四个参数。
如果不设置回调函数,可以把 CURLOPT_WRITEDATA 设置为一个C文件句柄,此时libcurl会把获取的数据输出到句柄打开的文件中。(所以这个属性默认大概是stdout)。
要注意的是,libcurl在获取到的内容较大时会将数据分段,多次调用回调函数。编写回调函数时要考虑到这一点。
下面是超时机制:
设置libcurl超时需要下面的函数
curl_easy_setopt(curl_handle, CURLOPT_TIMEOUT,超时时间);
超时时间以秒为单位,如果请求超时,curl_easy_perform返回28.
设置超时还可以设置 CURLOPT_TIMEOUT_MS 属性。单位毫秒。毫秒超时优先于秒超时。
(据说毫秒超时这玩意可能不太好用,而且超时功能并不完全线程安全)
以使用windows下的shadowsocks客户端为例。
设置方式:
curl_easy_setopt(curl_handle, CURLOPT_PROXY, "127.0.0.1:1080");
curl_easy_setopt(curl_handle, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5_HOSTNAME);
第一个表示代理服务器的地址和端口号,第二个表示代理所使用的协议。
shadowsocks默认本机端口为1080,可以在其目录下的user-wininet.json中的“ProxyServer”项和gui-config.json中的“localPort”项查看。
和不使用代理相比,个人暂时未发现其它代码上的修改。
由于vpn的工作方式足够底层,不需要专门对代理进行设置。(vpn都被封差不多了吧)
curl_easy_setopt(curl, CURLOPT_POST, 1);//设置请求方式为post
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data);//要post的数据,参数类型为char*。
没了。。。。
curl_easy_setopt(curl,CURLOPT_COOKIE,cookies);//参数类型char*
cookies格式应为 NAME=CONTENTS ,如果需要多个cookie,应用分号隔开,如:“ NAME1=CONTENTS1;NAME2=CONTENTS2; ”
还有一个属性是 CURLOPT_COOKIELIST ,功能更多,可自行查阅官网。(我没用过QAQ)
在编译时如果编译了Windows SSPI,openssl或其他支持的ssl库,进行一般的https访问基本不需要对代码做什么修改。
通常没必要对ssl的安全性进行检查,所以可以用以下代码忽略检查:
curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, false);//不验证ssl证书。
curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYHOST, false);//设置libcurl不验证host。忽略host验证必须同时忽略ssl证书验证。
libcurl超时机制不保证多线程安全,设置 CURLOPT_NOSIGNAL 为true可以一定程度上避免,但会导致dns解析失去超时功能。
官方建议是使用c-ares进行异步解析。我没写过,不做介绍。
openssl也不支持并发,要自己加锁。
可以摸鱼下载个小说,批量扒个图片。
或者写爬虫,论坛管理器等网络相关小工具。
比如不知道有没有下次的luogu冬日绘版的外挂。
就算不摸鱼,学些新东西也终归是好的嘛。
有没有夏日绘板啊【逃
libcurl下载百度贴吧帖子(只看楼主模式)中的图片:
vs2015project 链接 提取码:d6n8
请自觉尊重版权,不要下载了大大的图还未经许可四处乱发。
libcurl封禁贴吧账号一天(在有管理权限的贴吧):
vs2015project 链接 提取码:srov
为保证安全,程序默认使用的用户cookie(即 _BASICBDUSS )被从代码里删除了。