C++⽤libcurl库进⾏http通讯⽹络编程
⽬录索引:
⼀、LibCurl基本编程框架
⼆、⼀些基本的函数
三、curl_easy_setopt函数部分选项介绍
四、curl_easy_perform 函数说明(error 状态码)
五、libcurl使⽤的HTTP消息头
六、获取http应答头信息
七、多线程问题
⼋、什么时候libcurl⽆法正常⼯作
九、关于密码
⼗、HTTP验证
⼗⼀、代码⽰例
1.基本的http GET/POST操作
2获取html⽹页
3⽹页下载保存实例
4进度条实例显⽰⽂件下载进度
5断点续传实例
⼀、LibCurl基本编程框架
是⼀个跨平台的⽹络协议库,⽀持http, https, ftp, gopher, telnet, dict, file, 和ldap 协议。libcurl同样⽀持HTTPS证书授权,HTTP POST, HTTP PUT, FTP 上传, HTTP基本表单上传,代理,cookies,和⽤户认证。想要知道更多关于libcurl的介绍,可以到官⽹上去了解,在这⾥不再详述。
win32版的libcurl下载地址:
在基于LibCurl的程序⾥,主要采⽤callback function (回调函数)的形式完成传输任务,⽤户在启动传输前设置好各类参数和回调函数,当满⾜条件时libcurl将调⽤⽤户的回调函数实现特定功能。下⾯是利⽤libcurl完成传输任务的流程:
1. 调⽤curl_global_init()初始化libcurl
2. 调⽤curl_easy_init()函数得到 easy interface型指针
3. 调⽤curl_easy_setopt()设置传输选项
4. 根据curl_easy_setopt()设置的传输选项,实现回调函数以完成⽤户特定任务
5. 调⽤curl_easy_perform()函数完成传输任务
6. 调⽤curl_easy_cleanup()释放内存
在整过过程中设置curl_easy_setopt()参数是最关键的,⼏乎所有的libcurl程序都要使⽤它。
⼆、⼀些基本的函数
1.CURLcode curl_global_init(long flags);
描述:
这个函数只能⽤⼀次。(其实在调⽤curl_global_cleanup 函数后仍然可再⽤)
如果这个函数在curl_easy_init函数调⽤时还没调⽤,它讲由libcurl库⾃动调⽤,所以多线程下最好主动调⽤该函数以防⽌在线程中
curl_easy_init时多次调⽤。
注意:虽然libcurl是线程安全的,但curl_global_init是不能保证线程安全的,所以不要在每个线程中都调⽤curl_global_init,应该将该函数的调⽤放在主线程中。
参数:flags
CURL_GLOBAL_ALL //初始化所有的可能的调⽤。
CURL_GLOBAL_SSL //初始化⽀持安全套接字层。
CURL_GLOBAL_WIN32 //初始化win32套接字库。
CURL_GLOBAL_NOTHING //没有额外的初始化。
2 void curl_global_cleanup(void);
描述:在结束libcurl使⽤的时候,⽤来对curl_global_init做的⼯作清理。类似于close的函数。
注意:虽然libcurl是线程安全的,但curl_global_cleanup是不能保证线程安全的,所以不要在每个线程中都调⽤curl_global_init,应该将该函数的调⽤放在主线程中。
3 char *curl_version( );
描述: 打印当前libcurl库的版本。
4 CURL *curl_easy_init( );
描述:
curl_easy_init⽤来初始化⼀个CURL的指针(有些像返回FILE类型的指针⼀样). 相应的在调⽤结束时要⽤curl_easy_cleanup函数清理.
⼀般curl_easy_init意味着⼀个会话的开始. 它会返回⼀个easy_handle(CURL*对象), ⼀般都⽤在easy系列的函数中.
5 void curl_easy_cleanup(CURL *handle);
描述:
这个调⽤⽤来结束⼀个会话.与curl_easy_init配合着⽤.
参数:
CURL类型的指针.
6 CURLcode curl_easy_setopt(CURL *handle, CURLoption option, parameter);
描述: 这个函数最重要了.⼏乎所有的curl 程序都要频繁的使⽤它.它告诉curl库.程序将有如何的⾏为. ⽐如要查看⼀个⽹页的html代码等.(这个函数有些像ioctl函数)参数:
1 CURL类型的指针
2 各种CURLoption类型的选项.(都在curl.h库⾥有定义,man 也可以查看到)
3 parameter 这个参数既可以是个函数的指针,也可以是某个对象的指针,也可以是个long型的变量.它⽤什么这取决于第⼆个参数. CURLoption 这个参数的取值很多.具体的可以查看man⼿册.
7 CURLcode curl_easy_perform(CURL *handle);
描述:这个函数在初始化CURL类型的指针以及curl_easy_setopt完成后调⽤. 就像字⾯的意思所说perform就像是个舞台.让我们设置的option 运作起来.参数:
CURL类型的指针.
三、curl_easy_setopt函数部分选项介绍
本节主要介绍curl_easy_setopt中跟http相关的参数。该函数是curl中⾮常重要的函数,curl所有设置都是在该函数中完成的,该函数的设置选项众多,注意本节的阐述的只是部分常见选项。
1. CURLOPT_URL
设置访问URL
2. CURLOPT_WRITEFUNCTION,CURLOPT_WRITEDATA
回调函数原型为:size_t function( void *ptr, size_t size, size_t nmemb, void *stream);函数将在libcurl接收到数据后被调⽤,因此函数多做数据保存的功能,如处理下载⽂件。CURLOPT_WRITEDATA ⽤于表明CURLOPT_WRITEFUNCTION函数中的stream指针的来源。
如果你没有通过CURLOPT_WRITEFUNCTION属性给easy handle设置回调函数,libcurl会提供⼀个默认的回调函数,它只是简单的将接收到的数据打印到标准输出。你也可以通过 CURLOPT_WRITEDATA属性给默认回调函数传递⼀个已经打开的⽂件指针,⽤于将数据输出到⽂件⾥。
3. CURLOPT_HEADERFUNCTION,CURLOPT_HEADERDATA
回调函数原型为 size_t function( void *ptr, size_t size,size_t nmemb, void *stream); libcurl⼀旦接收到http 头部数据后将调⽤该函数。CURLOPT_WRITEDATA 传递指针给libcurl,该指针表明CURLOPT_HEADERFUNCTION 函数的stream指针的来源。
4. CURLOPT_READFUNCTION CURLOPT_READDATA
libCurl需要读取数据传递给远程主机时将调⽤CURLOPT_READFUNCTION指定的函数,函数原型是:size_t function(void *ptr, size_t size, size_t nmemb,void *stream). CURLOPT_READDATA 表明CURLOPT_READFUNCTION函数原型中的stream指针来源。
5. CURLOPT_NOPROGRESS,CURLOPT_PROGRESSFUNCTION,CURLOPT_PROGRESSDATA
跟数据传输进度相关的参数。CURLOPT_PROGRESSFUNCTION 指定的函数正常情况下每秒被libcurl调⽤⼀次,为了使
CURLOPT_PROGRESSFUNCTION被调⽤,CURLOPT_NOPROGRESS必须被设置为false,CURLOPT_PROGRESSDATA指定的参数将作为CURLOPT_PROGRESSFUNCTION指定函数的第⼀个参数
6. CURLOPT_TIMEOUT,CURLOPT_CONNECTIONTIMEOUT:
CURLOPT_TIMEOUT 由于设置传输时间,CURLOPT_CONNECTIONTIMEOUT 设置连接等待时间
7. CURLOPT_FOLLOWLOCATION
设置重定位URL
8. CURLOPT_RANGE: CURLOPT_RESUME_FROM:
断点续传相关设置。CURLOPT_RANGE 指定char *参数传递给libcurl,⽤于指明http域的RANGE头域,例如:
表⽰头500个字节:bytes=0-499
表⽰第⼆个500字节:bytes=500-999
表⽰最后500个字节:bytes=-500
表⽰500字节以后的范围:bytes=500-
第⼀个和最后⼀个字节:bytes=0-0,-1
同时指定⼏个范围:bytes=500-600,601-999
CURLOPT_RESUME_FROM 传递⼀个long参数给libcurl,指定你希望开始传递的偏移量。
四、curl_easy_perform函数说明(error 状态码)
该函数是完成curl_easy_setopt指定的所有选项,本节重点介绍curl_easy_perform的返回值。返回0意味⼀切ok,⾮0代表错误发⽣。主要错误码说明:
1. CURLE_OK
任务完成⼀切都好
2 CURLE_UNSUPPORTED_PROTOCOL
不⽀持的协议,由URL的头部指定
3 CURLE_COULDNT_CONNECT
不能连接到remote 主机或者代理
4 CURLE_REMOTE_ACCESS_DENIED
访问被拒绝
5 CURLE_HTTP_RETURNED_ERROR
Http返回错误
6 CURLE_READ_ERROR
读本地⽂件错误
要获取详细的错误描述字符串,可以通过const char *curl_easy_strerror(CURLcode errornum )这个函数取得.
五、libcurl使⽤的HTTP消息头
当使⽤libcurl发送http请求时,它会⾃动添加⼀些http头。我们可以通过CURLOPT_HTTPHEADER属性⼿动替换、添加或删除相应的HTTP消息头。
Host
http1.1(⼤部分http1.0)版本都要求客户端请求提供这个信息头。
Pragma
"no-cache"。表⽰不要缓冲数据。
Accept
"*/*"。表⽰允许接收任何类型的数据。
Expect
以POST的⽅式向HTTP服务器提交请求时,libcurl会设置该消息头为"100-continue",它要求服务器在正式处理该请求之前,返回⼀个"OK"消息。如果POST的数据很⼩,libcurl可能不会设置该消息头。
⾃定义选项
当前越来越多的协议都构建在HTTP协议之上(如:soap),这主要归功于HTTP的可靠性,以及被⼴泛使⽤的代理⽀持(可以穿透⼤部分防⽕墙)。这些协议的使⽤⽅式与传统HTTP可能有很⼤的不同。对此,libcurl作了很好的⽀持。
⾃定义请求⽅式(CustomRequest)
HTTP⽀持GET, HEAD或者POST提交请求。可以设置CURLOPT_CUSTOMREQUEST来设置⾃定义的请求⽅式,libcurl默认以GET⽅式提交请求:
curl_easy_setopt(easy_handle, CURLOPT_CUSTOMREQUEST, "MYOWNREQUEST");
修改消息头
HTTP协议提供了消息头,请求消息头⽤于告诉服务器如何处理请求;响应消息头则告诉浏览器如何处理接收到的数据。在libcurl中,你可以⾃由的添加这些消息头:
struct curl_slist *headers=NULL; /* init to NULL is important */
headers = curl_slist_append(headers, "Hey-server-hey: how are you?");
headers = curl_slist_append(headers, "X-silly-content: yes");
/* pass our list of custom made headers */
curl_easy_setopt(easyhandle, CURLOPT_HTTPHEADER, headers);
curl_easy_perform(easyhandle); /* transfer http */
curl_slist_free_all(headers); /* free the header list */
对于已经存在的消息头,可以重新设置它的值:
headers = curl_slist_append(headers, "Accept: Agent-007");
headers = curl_slist_append(headers, "Host: munged.host.line");
删除消息头
对于⼀个已经存在的消息头,设置它的内容为空,libcurl在发送请求时就不会同时提交该消息头:
headers = curl_slist_append(headers, "Accept:");
六、获取http应答头信息
发出http请求后,服务器会返回应答头信息和应答数据,如果仅仅是打印应答头的所有内容,则直接可以通过curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, 打印函数)的⽅式来完成,这⾥需要获取的是应答头中特定的信息,⽐如应答码、cookies列表等,则需要通过下⾯这个函数:
CURLcode curl_easy_getinfo(CURL *curl, CURLINFO info, ... );
info参数就是我们需要获取的内容,下⾯是⼀些参数值:
1.CURLINFO_RESPONSE_CODE
获取应答码
2.CURLINFO_HEADER_SIZE
头⼤⼩
3.CURLINFO_COOKIELIST
cookies列表
除了获取应答信息外,这个函数还能获取curl的⼀些内部信息,如请求时间、连接时间等等。
更多的参数可以参考API⽂档。
七、多线程问题网页html下载
⾸先⼀个基本原则就是:绝对不应该在线程之间共享同⼀个libcurl handle(CURL *对象),不管是easy handle还是multi handle(本⽂只介绍easy_handle)。⼀个线程每次只能使⽤⼀个handle。
libcurl是线程安全的,但有两点例外:信号(signals)和SSL/TLS handler。信号⽤于超时失效名字解析(timing out name resolves)。libcurl
依赖其他的库来⽀持SSL/STL,所以⽤多线程的⽅式访问HTTPS或FTPS的URL时,应该满⾜这些库对多线程操作的⼀些要求。详细可以参考:
OpenSSL:
GnuTLS:
NSS: 宣称是多线程安全的。
⼋、什么时候libcurl⽆法正常⼯作
传输失败总是有原因的。你可能错误的设置了⼀些libcurl的属性或者没有正确的理解某些属性的含义,或者是远程主机返回⼀些⽆法被正确解析的内容。
这⾥有⼀个黄⾦法则来处理这些问题:将CURLOPT_VERBOSE属性设置为1,libcurl会输出通信过程中的⼀些细节。如果使⽤的是http协议,请求头/响应头也会被输出。将CURLOPT_HEADER设为1,这些头信息将出现在消息的内容中。
当然不可否认的是,libcurl还存在bug。
如果你对相关的协议了解越多,在使⽤libcurl时,就越不容易犯错。
九、关于密码
客户端向服务器发送请求时,许多协议都要求提供⽤户名与密码。libcurl提供了多种⽅式来设置它们。
⼀些协议⽀持在URL中直接指定⽤户名和密码,类似于: protocol://user:password@example/path/。libcurl能正确的识别这种URL中的⽤户名与密码并执⾏相应的操作。如果你提供的⽤户名和密码中有特殊字符,⾸先应该对其进⾏URL编码。
也可以通过CURLOPT_USERPWD属性来设置⽤户名与密码。参数是格式如 “user:password ”的字符串:
curl_easy_setopt(easy_handle, CURLOPT_USERPWD, "user_name:password");
有时候在访问代理服务器的时候,可能时时要求提供⽤户名和密码进⾏⽤户⾝份验证。这种情况下,libcurl提供了另⼀个属性CURLOPT_PROXYUSERPWD:
curl_easy_setopt(easy_handle, CURLOPT_PROXYUSERPWD, "user_name:password");
在UNIX平台下,访问FTP的⽤户名和密码可能会被保存在$HOME/rc⽂件中。libcurl⽀持直接从这个⽂件中获取⽤户名与密码:
curl_easy_setopt(easy_handle, CURLOPT_NETRC, 1L);
在使⽤SSL时,可能需要提供⼀个私钥⽤于数据安全传输,通过CURLOPT_KEYPASSWD来设置私钥:
curl_easy_setopt(easy_handle, CURLOPT_KEYPASSWD, "keypassword");
⼗、HTTP验证
在使⽤HTTP协议时,客户端有很多种⽅式向服务器提供验证信息。默认的 HTTP验证⽅法是"Basic”,
它将⽤户名与密码以明⽂的⽅式、经Base64编码后保存在HTTP请求头中,发往服务器。当然这不太安全。
当前版本的libcurl⽀持的验证⽅法有:basic, Digest, NTLM, Negotiate, GSS-Negotiate and SPNEGO。(译者感叹:搞Web这么多年,尽然不知道这些Http的验证⽅式,实在惭愧。)可以通过CURLOPT_HTTPAUTH属性来设置具体的验证⽅式:
curl_easy_setopt(easy_handle, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST);
向代理服务器发送验证信息时,可以通过CURLOPT_PROXYAUTH设置验证⽅式:
curl_easy_setopt(easy_handle, CURLOPT_PROXYAUTH, CURLAUTH_NTLM);
也可以同时设置多种验证⽅式(通过按位与),使⽤‘CURLAUTH_ANY‘将允许libcurl可以选择任何它所⽀持的验证⽅式。通过CURLOPT_HTTPAUTH或 CURLOPT_PROXYAUTH属性设置的多种验证⽅式,libcurl会在运⾏时选择⼀种它认为是最好的⽅式与服务器通信:
curl_easy_setopt(easy_handle, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST|CURLAUTH_BASIC);
// curl_easy_setopt(easy_handle, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
⼗⼀、代码⽰例
下载的libcurl中⾃带了很多⽰例代码,在docs\examples⽬录下,建议下载该库后好好阅读⼀下这些代码。
libcurl的API⽂档在docs\libcurl\index.html中。
1.基本的http GET/POST操作
#include <stdio.h>
#include <curl/curl.h>
bool getUrl(char *filename)
{
CURL *curl;
CURLcode res;
FILE *fp;
if ((fp = fopen(filename, "w")) == NULL) // 返回结果⽤⽂件存储
return false;
struct curl_slist *headers = NULL;
headers = curl_slist_append(headers, "Accept: Agent-007");
curl = curl_easy_init(); // 初始化
if (curl)
{
//curl_easy_setopt(curl, CURLOPT_PROXY, "10.99.60.201:8080");// 代理
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);// 改协议头
curl_easy_setopt(curl, CURLOPT_URL,"www.baidu");
curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp); //将返回的http头输出到fp指向的⽂件
curl_easy_setopt(curl, CURLOPT_HEADERDATA, fp); //将返回的html主体数据输出到fp指向的⽂件
res = curl_easy_perform(curl); // 执⾏
if (res != 0) {
curl_slist_free_all(headers);
curl_easy_cleanup(curl);
}
fclose(fp);
return true;
}
}
bool postUrl(char *filename)
{
CURL *curl;
CURLcode res;
FILE *fp;
if ((fp = fopen(filename, "w")) == NULL)
return false;
curl = curl_easy_init();
if (curl)
{
curl_easy_setopt(curl, CURLOPT_COOKIEFILE, "/"); // 指定cookie⽂件
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, "&logintype=uid&u=xieyan&psw=xxx86"); // 指定post内容 //curl_easy_setopt(curl, CURLOPT_PROXY, "10.99.60.201:8080");
curl_easy_setopt(curl, CURLOPT_URL, " mail.sina/i "); // 指定url
curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
res = curl_easy_perform(curl);
curl_easy_cleanup(curl);
}
fclose(fp);
return true;
}
int main(void)
{
getUrl("/tmp/get.html");
postUrl("/tmp/post.html");
}
编译gcc get_post.c -o get_post –lcurl
./ get_post
2 获取html⽹页
#include <stdio.h>
#include <curl/curl.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
CURL *curl; //定义CURL类型的指针
CURLcode res; //定义CURLcode类型的变量,保存返回状态码
if(argc!=2)
{
printf("Usage : file <url>;\n");
exit(1);
}
curl = curl_easy_init(); //初始化⼀个CURL类型的指针
if(curl!=NULL)
{
//设置curl选项. 其中CURLOPT_URL是让⽤户指定url. argv[1]中存放的命令⾏传进来的⽹址
curl_easy_setopt(curl, CURLOPT_URL, argv[1]);
/
/调⽤curl_easy_perform 执⾏我们的设置.并进⾏相关的操作. 在这⾥只在屏幕上显⽰出来.
res = curl_easy_perform(curl);
//清除curl操作.
curl_easy_cleanup(curl);
}
return0;
}
编译gcc get_http.c -o get_http –lcurl
./ get_http
3 ⽹页下载保存实例
// 采⽤CURLOPT_WRITEFUNCTION 实现⽹页下载保存功能
#include <stdio.h>;
#include <stdlib.h>;
#include <unistd.h>;
#include <curl/curl.h>;
#include <curl/types.h>;
#include <curl/easy.h>;
FILE *fp; //定义FILE类型指针
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论