libcurl 编程指南

socket、http、ssl、libcurl逻辑关系

socket是tcp、udp连接基石。TSL是加密通信,使用openssl库加密通信。

有了socket连接,可以自己组应用层协议,实现http、mqtt等通信。可以通过移植libcurl、mqtt库等来更方便实现应用层网络传输。

libcurl简介

1 简介

参考:https://blog.csdn.net/huohongpeng/category_10856701.html

1.1 支持协议

libcurl库支持这些应用层协议:DICT, FILE, FTP, FTPS, GOPHER, GOPHERS, HTTP, HTTPS, IMAP, IMAPS, LDAP, LDAPS, MQTT, POP3, POP3S, RTMP, RTMPS, RTSP, SCP, SFTP, SMB, SMBS, SMTP, SMTPS, TELNET and TFTP。curl 支持 SSL 认证, libcurl同样支持HTTPS证书授权,HTTP POST, HTTP PUT, FTP 上传, HTTP基本表单上传,代理,cookies,和用户认证。

libcurl是高度可移植的, 它可以在很多平台上编译和运行, 包括Solaris, NetBSD, FreeBSD, OpenBSD, Darwin, HPUX, IRIX, AIX, Tru64, Linux, UnixWare, HURD, Windows, Amiga, OS/2, BeOs, Mac OS X, Ultrix, QNX, OpenVMS, RISC OS, Novell NetWare, DOS 等等…

libcurl是免费的,线程安全的,IPv6兼容,特性丰富,快速并且已经被很多成功知名的大公司应用。

1.2 参考网址
1.2.1 libcurl网站的入口

https://curl.se/libcurl/

1.2.2 《Everything curl 》书网页版,其中libcurl basics和HTTP with libcurl章节对编程有很大的参考意义
https://everything.curl.dev/

1.2.3 libcurl的编程示例,常用的功能都有例程源码
https://curl.se/libcurl/c/example.html

1.2.4 curl_easy_setopt()函数的选项说明
https://curl.se/libcurl/c/curl_easy_setopt.html

2 Easy和Multi

Easy接口是同步的,高效的,可以快速应用,大量的应用是基于这些接口的。Multi接口功能上和Easy没有区别,但是Multi是异步的,Easy同步的,也就是说只能一次完成一个传输,要想开始新的传输,就得等之前的传输完成,有点类似Linux中的阻塞执行。

相反Multi是异步的有点类似非阻塞执行,就像Linux下使用select或者poll在单线程下,实现多路复用的功能。我自己觉得Easy更常用,如果需要多次执行可以使用Easy结合多线程编程,libcurl是线程安全的。我的文章也是重点介绍Easy相关的接口,不介绍Multi接口。

3 Easy接口的使用

  • 第一步 使用curl_easy_init()初始化你的easy会话(easy-session) 并且可以获得一个句柄(handle),这个句柄作为其他Easy接口函数的输入参数。
  • 第二步 使用curl_easy_setopt()设置一些传输相关选项,最重要的就是URL,然后可以设置一些回调函数,当数据有效时,这些回调函数可以被调用。这个函数非常要,有点类似Linux下的ioctl()函数,后面会结合例子说明。
  • 第三步 设置好所有的参数以后,调用curl_easy_perform()去执行这个传输,这个函数会执行所有的操作,直到执行完以后或者出错了返回,所以这个函数是阻塞执行的,也就是同步执行的。
  • 第四步 执行完成以后,你也可以使用curl_easy_getinfo()获取传输相关的信息,比如获取整个传输用了多长时间。
  • 第五步 当所有传输结束以后可以调用curl_easy_cleanup()释放之前申请的会话。

当然在使用上面这些函数之前需要对整个库进行初始化,用到的函数就是curl_global_init(),整个库都不再使用了或者程序要结束了调用curl_global_cleanup()去释放所有资源。

4 其他常用接口
除了常用的传输相关的函数还有一些常用的辅助函数:

curl_version()    		//返回libcurl版本相关信息的指针
curl_getdate()    		//转换一个日期 string 到 time_t
curl_mime_init()    	//一些mime系列函数对于HTTP POST表单的操作
curl_formadd()    		//也是HTTP POST表单的操作,但是这个接口比较老了
curl_slist_append()    	//建立一个清单的链表,最常用的就是HTTP头的添加和删除。
curl_slist_free_all()   //释放通过curl_slist_append()创建的curl_slist
curl_easy_escape()    	//URL编码
curl_easy_unescape()    //URL解码

5 总结

这篇文章只是简单的对libcurl的一个整体介绍,后面针对具体的函数和例程进行讲解

libcurl接口函数curl_global_init/cleanup

名称:curl_global_init

原型

#include <curl/curl.h>
CURLcode curl_global_init(long flags);

描述
这个函数设置libcurl需要的程序运行环境,可以把它看作一个的加载器扩展

在进程环境(所有代码共享内存地址空间的程序)下,libcurl其他所有函数被调用之前这函数至少需要被调用一次。在进程的生命周期内,这个环境设置是没有变化的,所以在同一个程序中,多次调用和只调用一次没有区别。

这个flags选项精确的告诉libcurl那些特性需要被初始化,这些特性可以按位异或,一般情况下都是使用 CURL_GLOBAL_ALL这个标志。除非你非常了解这个哭的内部原理,否则不建议使用其他参数。

这个函数不是线程安全的。千万不要在进程中的其他线程里面去调用。这并不是说使用libcurl不能使用多线程。curl_global_init()不是线程安全的,主要是因为它调用了其他函数库中的函数,而这些函数可能不是线程安全的,一般情况都是在进程刚开始的时候,没有创建其他线程之前调用curl_global_init()。

FLAGS

CURL_GLOBAL_ALL
CURL_GLOBAL_SSL
CURL_GLOBAL_WIN32
CURL_GLOBAL_NOTHING
CURL_GLOBAL_DEFAULT
CURL_GLOBAL_ACK_EINTR

返回值
当发生错误时会返回非0值,如果返回非0值,代表初始化失败了,不要使libcurl的其他功能

名称: curl_global_cleanup

原型

#include <curl/curl.h>
void curl_global_cleanup(void);

描述
这个函数释放 curl_global_init()已经获得的所有资源。同样这个函数也不是线程安全的。

警告
curl_global_cleanup不阻塞的等待任何被libcurl创建的线程终止(例如name resolving线程)。如果一个包含libcurl被动态的卸载,但是libcurl创建的线程仍然运行,这可能引起程序的崩溃。一般在整个程序(进程)结束之前调用这个函数释放资源,就不会有问题。

libcurl接口函数curl_easy_init

名称:curl_easy_init

原型

#include <curl/curl.h>
CURL *curl_easy_init( );

描述
这个函数必须首先被调用,他返回CURL easy会话的一个句柄,这个句柄作为其他easy相关接口函数的输入参数。当所有操作完成后,必须对应的调用 curl_easy_cleanup函数释放资源,也就是说

curl_easy_init和curl_easy_cleanup成对出现,就像malloc和free。

如果在curl_easy_init之前没有调用curl_global_init,那么curl_easy_init会自动的调用curl_easy_init。但是在多线程情况下这是致命的错误,因为curl_easy_init不是线程安全的。也能会会引起资源泄露,因为没有对应的curl_global_cleanup释放资源。

强烈的建议你适当的显示调用curl_global_init,不要允许自动的行为。一般都是在进程开始的时候显示的调用curl_global_init,在进程退出之前显示的调用curl_global_cleanup。

返回值
如果这个函数返回NULL,说明发生了错误,不在在使用其他的curl函数

示例

CURL *curl = curl_easy_init();
if(curl) 
{
  CURLcode res;
  curl_easy_setopt(curl, CURLOPT_URL, "https://example.com");
  res = curl_easy_perform(curl);
  curl_easy_cleanup(curl);
}

libcurl接口函数curl_easy_cleanup

名称: curl_easy_cleanup

原型

#include <curl/curl.h>

void curl_easy_cleanup(CURL *handle);

描述
这个函数必须在整个easy会话的最后被调用,它正好和curl_easy_init相反。这个函数可能关闭和这个句柄相关的所有连接。如果你打算传输多个文件,请不要每次都调用这个函数,这个句柄打开以后,重复使用这个句柄传输所有文件,都传输完成以后,在调用这个函数,释放这个句柄。重复使用句柄是libcurl高性能使用的关键。

这个函数也可以在progress callback 或者header callback中调用,这样可以终止连接。

(句柄)handle被这个函数清理以后,在继续使用都是非法的。curl_easy_cleanup已经清理掉了这个句柄和这个句柄相关的所有内存。

如果这个函数输入的句柄是一个空指针,这个函数会立即返回。

示例

CURL *curl = curl_easy_init();
if(curl) 
{
  CURLcode res;
  curl_easy_setopt(curl, CURLOPT_URL, "https://example.com");
  res = curl_easy_perform(curl);
  curl_easy_cleanup(curl);
}

libcurl接口函数curl_easy_setopt

名称: curl_easy_setopt

原型

#include <curl/curl.h>
CURLcode curl_easy_setopt(CURL *handle, CURLoption option, parameter);

描述
curl_easy_setopt 用于告诉libcurl需要做什么,或者是告诉libcurl需要完成哪些任务。通过设置合适的选项(options),应用可以改变libcurl的行为。所有被设置的选项后面都会跟随一个参数(parameter)。参数可以是一个long型变量,函数指针,一个对象(结构体)指针或是一个curl_off_t变量,跟随的这个参数是什么取决于选项(option)。需要仔细的阅读这个手册,因为一个不合理的输入值可能引起libcurl的行为异常。你可以在每次函数调用只设置一个选项。典型的应用程会在设置阶段多次调用curl_easy_setopt。通过这个函数设置handle的选项,对于后面使用这个handle来执行的传输都是有效的。在每一次传输完成后这些选项不会被复位,所以如果你想在后面的传输使用不同的选项,你可以在本次传输完成后,下一次传输执行前,通过调用curl_easy_setopt 来改变选项。也可以使用curl_easy_reset复位所有选型到默认值。

字符串作为’char *'类型传给libcurl,libcurl会对字符串进行拷贝;因此在curl_easy_setopt返回后,参数指针指向的字符串存储空间可以被复写或者释放。只有CURLOPT_POSTFILEDS这种情况除外。但是可以使用CURLOPT_COPYPOSTFILEDS作为替代。curl_easy_setopt不接受大小超过8M长度的字符串。

对于选项设置的顺序是没有要求的。

在7.17.0以前的版本,字符串是不被开拷贝的。所以强制用户保持字符串是有效的指导这些字符串不再需要。

这个handle是从curl_easy_init或者curl_easy_duphandle的调用返回的。

curl_easy_setopt有很多很多的选项,我在后面结合实际使用在做具体的介绍。

CURLOPT_VERBOSE
CURLOPT_WRITEFUNCTION
CURLOPT_WRITEDATA
CURLOPT_READFUNCTION
CURLOPT_READDATA
...
...

返回值
CURL_OK(0)代表使用了恰当的设置,如果是非0值意味着有错误发生。

如果你设置的选项libcurl不知道是什么,有以下可能的原因:

如果返回CURLE_UNKNOWN_OPTION,可能的原因是这个选项是老版本的库使用的,在新版本库用被移除了。

如果返回CURL_NOT_BUILD_IN,是因为在编译的时候禁止了相关的功能。

示例

CURL *curl = curl_easy_init();
if(curl) 
{
  CURLcode res;
  curl_easy_setopt(curl, CURLOPT_URL, "https://example.com");
  res = curl_easy_perform(curl);
  curl_easy_cleanup(curl);
}

libcurl接口函数curl_easy_perform

名称:curl_easy_perform

原型

#include <curl/curl.h>
CURLcode curl_easy_perform(CURL *easy_handle);

描述
这个函数在curl_easy_init和所有的curl_easy_setopt参数设置完成后调用。调用这个函数libcurl将按照设置的选项执行动作。这个函数需要使用curl_easy_init函数返回的相同的easy_handle作为输入被调用。

curl_easy_perform通过阻塞的方式执行全部的请求,当执行完以后或者失败了才返回。对于非阻塞的方式,可以参考curl_multi_perform.

你可以使用相同的easy_handle多次调用curl_easy_perform.如果你打算传输一个或更多个文件,更推荐你使用这种方式。对于之后的传输libcurl将尝试重用相同的链接,因此会更快,更少的使用cpu和网络资源。需要注意的是,使用curl_easy_setopt在两次curl_easy_perform去设置选项(比如每个不同的传输肯定需要不同的CURLOPT_URL)。

相同easy_handle,绝不能在两个不同的地方同时调用curl_easy_perform。在下一调用的时候,必须让这个函数先返回,也换句话说就是按顺序阻塞的执行。如果想并行传输,必须使用多个easy_handle。

当easy_handle添加到multi handle中,他将不能在使用curl_easy_perform。

返回值
CURL_OK(0)意味着一切都是正常的,非0意味着发生了一个错误,错误定义在<curl/curl.h>中。可以在libcurl-erros这里去查看。如果通过curl_easy_setopt设置了CURLOPT_ERRORBUFFER选项,当返回非0值,一个可读的错误消息将放入error buffer中。

示例

CURL *curl = curl_easy_init();
if(curl) 
{
  CURLcode res;
  curl_easy_setopt(curl, CURLOPT_URL, "https://example.com");
  res = curl_easy_perform(curl);
  curl_easy_cleanup(curl);
}

libcurl接口函数curl_easy_getinfo

名称 curl_easy_getinfo
原型

#include <curl/curl.h>
CURLcode curl_easy_getinfo(CURL *curl, CURLINFO info, ... );

描述
使用这个函数从curl会话请求内部信息。第三个参数必须是一个long变量,一个指向char *的指针,一个指向struct curl_slist *指针或者一个指向double的指针(就像本文档下面将要描述的那样)。当这个函数返回CURL_OK时,这个被指向的数据会被相应的填充。

不用对这个函数返回的内存进行释放(free),除非在下面被特殊的强调。

有效的INFO
下面的信息可以被截取(有很多,可以去英文网站去看,有对应的示例代码):

CURLINFO_HTTP_VERSION 获取在连接时候使用的http版本。

CURL *curl = curl_easy_init();
if(curl) 
{
  CURLcode res;
  curl_easy_setopt(curl, CURLOPT_URL, "https://example.com");
  res = curl_easy_perform(curl);
  if(res == CURLE_OK) 
  {
    long http_version;
    curl_easy_getinfo(curl, CURLINFO_HTTP_VERSION, &http_version);
  }
  curl_easy_cleanup(curl);
}

CURLINFO_TOTAL_TIME 获取前一个传输的执行总时间

curl = curl_easy_init();
if(curl) 
{
  double total;
  curl_easy_setopt(curl, CURLOPT_URL, url);
  res = curl_easy_perform(curl);
  if(CURLE_OK == res) 
  {
    res = curl_easy_getinfo(curl, CURLINFO_TOTAL_TIME, &total);
    if(CURLE_OK == res) 
    {
      printf("Time: %.1f", total);
    }
  }
  /* always cleanup */
  curl_easy_cleanup(curl);
}

CURLINFO_CONNECT_TIME 获取connet从开始到完成的时间

curl = curl_easy_init();
if(curl) 
{
  double connect;
  curl_easy_setopt(curl, CURLOPT_URL, url);
  res = curl_easy_perform(curl);
  if(CURLE_OK == res) 
  {
    res = curl_easy_getinfo(curl, CURLINFO_CONNECT_TIME, &connect);
    if(CURLE_OK == res) 
    {
      printf("Time: %.1f", connect);
    }
  }
  /* always cleanup */
  curl_easy_cleanup(curl);
}

CURLINFO_SPEED_UPLOAD_T 获取上传速度,单位bytes/second

CURL *curl = curl_easy_init();
if(curl) 
{
  curl_easy_setopt(curl, CURLOPT_URL, "https://example.com");
 
  /* Perform the request */
  res = curl_easy_perform(curl);
 
  if(!res) 
  {
    curl_off_t speed;
    res = curl_easy_getinfo(curl, CURLINFO_SPEED_UPLOAD_T, &speed);
    if(!res) 
    {
      printf("Upload speed %" CURL_FORMAT_CURL_OFF_T " bytes/sec\n", speed);
    }
  }
}

关于时间
可以从curl_easy_getinfo()获取下面看到的6个时间值

curl_easy_perform()
    |
    |--NAMELOOKUP
    |--|--CONNECT
    |--|--|--APPCONNECT
    |--|--|--|--PRETRANSFER
    |--|--|--|--|--STARTTRANSFER
    |--|--|--|--|--|--TOTAL
    |--|--|--|--|--|--REDIRECT

返回值
如果操作成功,返回CURLE_OK

libcurl入门之http通信(1)——编程访问百度,将访问结果通过回调写入文件

1 摘要
1.1 平台

运行平台:君正x2000
libcurl版本:7.68.0,通过buildroot编译生成。
编译器:mips-linux-gnu-gcc (gcc version 7.2.0 (Ingenic r4.0.0-gcc720 2018.02-28))
编译主机:ubuntu 18.04

1.2 功能
http通信(1)

1.1最少的代码实现http访问百度,这个示例将通过get方法访问百度,并将百度返回的结果存入data.html文件中。

2 选项名:CURLOPT_URL
提供请求的URL

2.1 原型

#include <curl/curl.h>
CURLcode curl_easy_setopt(CURL *handle, CURLOPT_URL, char *URL);

2.2 描述
使用这个选项需要传入一个指向URL的指针。这个参数应该是一个char*有一个终止符(\0)的字符串,这些字符串必须使用下面这些格式的URL编码:

scheme://host:port/path

关于格式更详细的解释可以参考RFC 3986

libcurl执行传输(curl_easy_perform)之前不会检查URL的语法,也不会使用这个变量。所以无论你将URL设置成什么值,curl_easy_setopt都会正常返回CURL_OK.

如果URL中没有给出scheme名称(例如http://or ftp://等等),libcurl会根据host进行猜测。如果最外层sub-domain名称和 DICT, FTP, IMAP, LDAP, POP3 or SMTP匹配,那么这些协议将会被使用。其他情况会使用HTTP协议。在7.45.0版本如以后猜测功能将被会被设置默认协议禁止,对于细节可以查看CURLOPT_DEFAULT_PROTOCOL.

如果scheme指定的协议或者libcurl推理出来的协议如果libcurl不支持,那么在调用curl_easy_perform的时候会返回CURLE_UNSUPPORTED_PROTOCOL 错误。可以通过curl_version_info 查看具体的协议。

还可以通过CURLOPT_PROTOCOLS限制libcurl传输中使用的协议。对于从外部接收的URl进行限制,这是非常有用的。

如果设置了CURLOPT_CURLU,那么CURLOPT_URL将被忽略。

在开始传输之前必须要设置CURLOPT_CURLUCURLOPT_URL

在curl_easy_setopt设置之后,URL字符串可以被释放,不用保留。

2.3 示例

CURL *curl = curl_easy_init();
if(curl) 
{
  curl_easy_setopt(curl, CURLOPT_URL, "https://example.com");
 
  curl_easy_perform(curl);
}

3 选项名:CURLOPT_WRITEFUNCTION
设置被接收数据的写回调函数。(用于保存接收的数据)

3.1 原型

#include <curl/curl.h>
size_t write_callback(char *ptr, size_t size, size_t nmemb, void *userdata);
CURLcode curl_easy_setopt(CURL *handle, CURLOPT_WRITEFUNCTION, write_callback

3.2 描述
传入回调函数的指针,回调函数是上面描述的格式。

当被接收的数据需要保存就会调用这个回调函数。对于大部分传输,这个回调函数会被调用多次,并且每次一执行都会提供一部分数据块。ptr指向一个被接收的数据,nmemb是数据的大小,size总是1.

回调函数会在每次调用中传递尽可能多的数据,但是你不能预测。可能是1个字节,也可能是几千个字节。回调函数单次传递的body data(http的数据部分)的最大数据量定义在curl.h文件中的CURL_MAX_WRITE_SIZE(默认大小是16K)。如果CURLOPT_HEADER被使能,head部份的数据也会通过这个回调函数进行传递。传递的头的最大数据量定义在CURL_MAX_HTTP_HEADER 中。通常情况下是100K。

如果传输的文件是空的,这个函数也可能传递0个字节的数据。

被传递的到这个函数的数据,没有null终止符(\0).

可以通过CURLOPT_WRITEDATA选项设置userdata参数。

回调函数应该返回实际处理的字节数。如果传递的值和返回的值不同,那么将会向libcurl发送一个错误信号。这将会引起传输终止,并且执行函数将会返回一个CURLE_WRITE_ERROR错误。

如果回调函数返回CURL_WRITEFUNC_PAUSE 将会引起回调函数挂起。可以在curl_easy_pause查看具体的细节。

如果回调函数指针为空,libcurl将使用库内部的回调函数。默认的回调函数将写数据到CURLOPT_WRITEDATA指定的FILE*。

libcurl将使用’fwrite’作为内部默认的回调函数。

3.3 示例

struct memory 
{
   char *response;
   size_t size;
 };
 
 static size_t cb(void *data, size_t size, size_t nmemb, void *userp)
 {
   size_t realsize = size * nmemb;
   struct memory *mem = (struct memory *)userp;
 
   char *ptr = realloc(mem->response, mem->size + realsize + 1);
   if(ptr == NULL)
     return 0;  /* out of memory! */
 
   mem->response = ptr;
   memcpy(&(mem->response[mem->size]), data, realsize);
   mem->size += realsize;
   mem->response[mem->size] = 0;
 
   return realsize;
 }
 
 struct memory chunk;
 
 /* send all data to this function  */
 curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, cb);
 
 /* we pass our 'chunk' struct to the callback function */
 curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *)&chunk);

4 代码实现
Makefile

LIBS=-L../../../buildroot/output/target/usr/lib -lpthread -lz -lcurl -lcrypto -lssl 
CC_FLAG=-Wall -I../../../buildroot/output/staging/usr/include
SRC=curl_test.c
CC=mips-linux-gnu-gcc 
 
 
test : $(SRC)
	$(CC) $< $(LIBS) $(CC_FLAG) -O2 -o $@
	cp -v $@ ~/nfs
 
.PHONY : clean
 
clean :
	rm -rf test

curl_test.c

#include <stdio.h>
#include <curl/curl.h>
#include <string.h>
#include <time.h>
#include <stdlib.h>
 
static size_t baidu_get_test_cb(void *buffer, size_t size, size_t nmemb, void *user_p);
static void baidu_get_test(void);
 
 
int main()
{
	CURLcode code;
	code = curl_global_init(CURL_GLOBAL_ALL);
	
	if(CURLE_OK != code) 
	{
		printf("curl init Err\n");
		return -1;
	}
	
	/*
	 * get 方法访问百度并获取数据
	 */
	baidu_get_test();
	
	curl_global_cleanup();
 
	return 0;
}
 
 
static size_t baidu_get_test_cb(void *buffer, size_t size, size_t nmemb, void *user_p)
{
	FILE *fp = (FILE *)user_p;
	size_t return_size = fwrite(buffer, size, nmemb, fp);
	return return_size;
}
 
 
static void baidu_get_test(void)
{
	/*
	 * 获取一个easy句柄,后面通过这个句柄进行通信
	 */
	CURL *easy_handle = curl_easy_init();
 
	if(!easy_handle) 
	{
		printf("curl_easy_init Err \n");
		curl_global_cleanup();
		return ;
	}
 
	/*
	 * 打开一个data.html文件,用于存储百度返回的数据
	 */
	FILE *fp = fopen("data.html", "w+");
 
	/*
	 * 设置libcurl的选项,没有指定http访问方法,libcurl默认使用get方法
	 */
	curl_easy_setopt(easy_handle, CURLOPT_URL, "http://www.baidu.com");
	curl_easy_setopt(easy_handle, CURLOPT_WRITEFUNCTION, baidu_get_test_cb);
	curl_easy_setopt(easy_handle, CURLOPT_WRITEDATA, fp);
	
	/*
	 * 执行访问
	 */
	CURLcode code = curl_easy_perform(easy_handle);
	
	if(CURLE_OK != code) 
	{
		printf("curl_easy_perform %d\n", code);
	}
	
	fclose(fp);
	/*
	 * 最后记得释放句柄
	 */
	curl_easy_cleanup(easy_handle);	
}

执行结果

# cat data.html 
<!DOCTYPE html><!--STATUS OK-->
<html>
<head>
        <meta http-equiv="content-type" content="text/html;charset=utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=Edge">
        <link rel="dns-prefetch" href="//s1.bdstatic.com"/>
        <link rel="dns-prefetch" href="//t1.baidu.com"/>
        <link rel="dns-prefetch" href="//t2.baidu.com"/>
        <link rel="dns-prefetch" href="//t3.baidu.com"/>
        <link rel="dns-prefetch" href="//t10.baidu.com"/>
        <link rel="dns-prefetch" href="//t11.baidu.com"/>
        <link rel="dns-prefetch" href="//t12.baidu.com"/>
        <link rel="dns-prefetch" href="//b1.bdstatic.com"/>
        <title>百度一下,你就知道</title>
        <link href="http://s1.bdstatic.com/r/www/cache/static/home/css/index.css" rel="stylesheet" type="text/css" />
        <!--[if lte IE 8]><style index="index" >#content{height:480px\9}#m{top:260px\9}</style><![endif]-->
        <!--[if IE 8]><style index="index" >#u1 a.mnav,#u1 a.mnav:visited{font-family:simsun}</style><![endif]-->
        <script>var hashMatch = document.location.href.match(/#+(.*wd=[^&].+)/);if (hashMatch && hashMatch[0] && hashMatch[1]) {document.location.replace("http://"+location.host+"/s?"+hashMatch[1]);}var ns_c = function(){};</script>
        <script>function h(obj){obj.style.behavior='url(#default#homepage)';var a = obj.setHomePage('//www.baidu.com/');}</script>
        <noscript><meta http-equiv="refresh" content="0; url=/baidu.html?from=noscript"/></noscript>
        <script>window._ASYNC_START=new Date().getTime();</script>
</head>
<body link="#0000cc"><div id="wrapper" style="display:none;"><div id="u"><a href="//www.baidu.com/gaoji/preferences.html"  onmousedown="return user_c({'fm':'set','tab':'setting','login':'0'})">搜索设置</a>|<a id="btop" href="/"  onmousedown="return user_c({'fm':'set','tab':'index','login':'0'})">百度首页</a>|<a id="lb" href="https://passport.baidu.com/v2/?login&tpl=mn&u=http%3A%2F%2Fwww.baidu.com%2F" onclick="return false;"  onmousedown="return user_c({'fm':'set','tab':'login'})">登录</a><a href="https://passport.baidu.com/v2/?reg&regType=1&tpl=mn&u=http%3A%2F%2Fwww.baidu.com%2F"  onmousedown="return user_c({'fm':'set','tab':'reg'})" target="_blank" class="reg">注册</a></div><div id="head"><div class="s_nav"><a href="/" class="s_logo" onmousedown="return c({'fm':'tab','tab':'logo'})"><img src="//www.baidu.com/img/baidu_jgylogo3.gif" width="117" height="38" border="0" alt="到百度首页" title="到百度首页"></a><div class="s_tab" id="s_tab"><a href="http://news.baidu.com/ns?cl=2&rn=20&tn=news&word=" wdfield="word"  onmousedown="return c({'fm':'tab','tab':'news'})">新闻</a>&#12288;<b>网页</b>&#12288;<a href="http://tieba.baidu.com/f?kw=&fr=wwwt" wdfield="kw"  οnmοusedοwn="return c({'fm':'tab','tab':'tieba'})">贴吧</a>&#12288;<a href="http://zhidao.baidu.com/q?ct=17&pn=0&tn=ikaslist&rn=10&word=&fr=wwwt" wdfield="word"  οnmοusedοwn="return c({'fm':'tab','tab':'zhidao'})">知道</a>&#12288;<a href="http://music.baidu.com/search?fr=ps&key=" wdfield="key"  οnmοusedοwn="return c({'fm':'tab','tab':'music'})">音乐</a>&#12288;<a href="http://image.baidu.com/i?tn=baiduimage&ps=1&ct=201326592&lm=-1&cl=2&nc=1&word=" wdfield="word"  οnmοusedοwn="return c({'fm':'tab','tab':'pic'})">图片</a>&#12288;<a href="http://v.baidu.com/v?ct=301989888&rn=20&pn=0&db=0&s=25&word=" wdfield="word"   οnmοusedοwn="return c({'fm':'tab','tab':'video'})">视频</a>&#12288;<a href="http://map.baidu.com/m?word=&fr=ps01000" wdfield="word"  οnmοusedοwn="return c({'fm':'tab','tab':'map'})">地图</a>&#12288;<a href="http://wenku.baidu.com/search?word=&lm=0&od=0" wdfield="word"  οnmοusedοwn="return c({'fm':'tab','tab':'wenku'})">文库</a>&#12288;<a href="//www.baidu.com/more/"  οnmοusedοwn="return c({'fm':'tab','tab':'more'})">更多»</a></div></div><form id="form" name="f" action="/s" class="fm" ><input type="hidden" name="ie" value="utf-8"><input type="hidden" name="f" value="8"><input type="hidden" name="rsv_bp" value="1"><span class="bg s_ipt_wr"><input name="wd" id="kw" class="s_ipt" value="" maxlength="100"></span><span class="bg s_btn_wr"><input type="submit" id="su" value="百度一下" class="bg s_btn" οnmοusedοwn="this.className='bg s_btn s_btn_h'" οnmοuseοut="this.className='bg s_btn'"></span><span class="tools"><span id="mHolder"><div id="mCon"><span>输入法</span></div><ul id="mMenu"><li><a href="javascript:;" name="ime_hw">手写</a></li><li><a href="javascript:;" name="ime_py">拼音</a></li><li class="ln"></li><li><a href="javascript:;" name="ime_cl">关闭</a></li></ul></span><span class="shouji"><strong>推荐&nbsp;:&nbsp;</strong><a href="http://w.x.baidu.com/go/mini/8/10000020" οnmοusedοwn="return ns_c({'fm':'behs','tab':'bdbrowser'})">百度浏览器,打开网页快2秒!</a></span></span></form></div><div id="content"><div id="u1"><a href="http://news.baidu.com" name="tj_trnews" class="mnav">新闻</a><a href="http://www.hao123.com" name="tj_trhao123" class="mnav">hao123</a><a href="http://map.baidu.com" name="tj_trmap" class="mnav">地图</a><a href="http://v.baidu.com" name="tj_trvideo" class="mnav">视频</a><a href="http://tieba.baidu.com" name="tj_trtieba" class="mnav">贴吧</a><a href="https://passport.baidu.com/v2/?login&tpl=mn&u=http%3A%2F%2Fwww.baidu.com%2F" name="tj_login" id="lb" οnclick="return false;">登录</a><a href="//www.baidu.com/gaoji/preferences.html" name="tj_settingicon" id="pf">设置</a><a href="//www.baidu.com/more/" name="tj_briicon" id="bri">更多产品</a></div><div id="m"><p id="lg"><img src="//www.baidu.com/img/bd_logo.png" width="270" height="129"></p><p id="nv"><a href="http://news.baidu.com">新&nbsp;闻</a> <b>网&nbsp;页</b> <a href="http://tieba.baidu.com">贴&nbsp;吧</a> <a href="http://zhidao.baidu.com">知&nbsp;道</a> <a href="http://music.baidu.com">音&nbsp;乐</a> <a href="http://image.baidu.com">图&nbsp;片</a> <a href="http://v.baidu.com">视&nbsp;频</a> <a href="http://map.baidu.com">地&nbsp;图</a></p><div id="fm"><form id="form1" name="f1" action="/s" class="fm"><span class="bg s_ipt_wr"><input type="text" name="wd" id="kw1" maxlength="100" class="s_ipt"></span><input type="hidden" name="rsv_bp" value="0"><input type=hidden name=ch value=""><input type=hidden name=tn value="baidu"><input type=hidden name=bar value=""><input type="hidden" name="rsv_spt" value="3"><input type="hidden" name="ie" value="utf-8"><span class="bg s_btn_wr"><input type="submit" value="百度一下" id="su1" class="bg s_btn" οnmοusedοwn="this.className='bg s_btn s_btn_h'" οnmοuseοut="this.className='bg s_btn'"></span></form><span class="tools"><span id="mHolder1"><div id="mCon1"><span>输入法</span></div></span></span><ul id="mMenu1"><div class="mMenu1-tip-arrow"><em></em><ins></ins></div><li><a href="javascript:;" name="ime_hw">手写</a></li><li><a href="javascript:;" name="ime_py">拼音</a></li><li class="ln"></li><li><a href="javascript:;" name="ime_cl">关闭</a></li></ul></div><p id="lk"><a href="http://baike.baidu.com">百科</a> <a href="http://wenku.baidu.com">文库</a> <a href="http://www.hao123.com">hao123</a><span>&nbsp;|&nbsp;<a href="//www.baidu.com/more/">更多&gt;&gt;</a></span></p><p id="lm"></p></div></div><div id="ftCon"><div id="ftConw"><p id="lh"><a id="seth" onClick="h(this)" href="/" οnmοusedοwn="return ns_c({'fm':'behs','tab':'homepage','pos':0})">把百度设为主页</a><a id="setf" href="//www.baidu.com/cache/sethelp/index.html" οnmοusedοwn="return ns_c({'fm':'behs','tab':'favorites','pos':0})" target="_blank">把百度设为主页</a><a οnmοusedοwn="return ns_c({'fm':'behs','tab':'tj_about'})" href="http://home.baidu.com">关于百度</a><a οnmοusedοwn="return ns_c({'fm':'behs','tab':'tj_about_en'})" href="http://ir.baidu.com">About Baidu</a></p><p id="cp">&copy;2018&nbsp;Baidu&nbsp;<a href="/duty/" name="tj_duty">使用百度前必读</a>&nbsp;京ICP证030173号&nbsp;<img src="http://s1.bdstatic.com/r/www/cache/static/global/img/gs_237f015b.gif"></p></div></div><div id="wrapper_wrapper"></div></div><div class="c-tips-container" id="c-tips-container"></div>
<script>window.__async_strategy=2;</script>
<script>var bds={se:{},su:{urdata:[],urSendClick:function(){}},util:{},use:{},comm : {domain:"http://www.baidu.com",ubsurl : "http://sclick.baidu.com/w.gif",tn:"baidu",queryEnc:"",queryId:"",inter:"",templateName:"baidu",sugHost : "http://suggestion.baidu.com/su",query : "",qid : "",cid : "",sid : "",indexSid : "",stoken : "",serverTime : "",user : "",username : "",loginAction : [],useFavo : "",pinyin : "",favoOn : "",curResultNum:"",rightResultExist:false,protectNum:0,zxlNum:0,pageNum:1,pageSize:10,newindex:0,async:1,maxPreloadThread:5,maxPreloadTimes:10,preloadMouseMoveDistance:5,switchAddMask:false,isDebug:false,ishome : 1},_base64:{domain : "http://b1.bdstatic.com/",b64Exp : -1,pdc : 0}};var name,navigate,al_arr=[];var selfOpen = window.open;eval("var open = selfOpen;");var isIE=navigator.userAgent.indexOf("MSIE")!=-1&&!window.opera;var E = bds.ecom= {};bds.se.mon = {'loadedItems':[],'load':function(){},'srvt':-1};try {bds.se.mon.srvt = parseInt(document.cookie.match(new RegExp("(^| )BDSVRTM=([^;]*)(;|$)"))[2]);document.cookie="BDSVRTM=;expires=Sat, 01 Jan 2000 00:00:00 GMT"; }catch(e){}</script>
<script>if(!location.hash.match(/[^a-zA-Z0-9]wd=/)){document.getElementById("ftCon").style.display='block';document.getElementById("u1").style.display='block';document.getElementById("content").style.display='block';document.getElementById("wrapper").style.display='block';setTimeout(function(){try{document.getElementById("kw1").focus();document.getElementById("kw1").parentNode.className += ' iptfocus';}catch(e){}},0);}</script>
<script type="text/javascript" src="http://s1.bdstatic.com/r/www/cache/static/jquery/jquery-1.10.2.min_f2fb5194.js"></script>
<script>(function(){var index_content = $('#content');var index_foot= $('#ftCon');var index_css= $('head [index]');var index_u= $('#u1');var result_u= $('#u');var wrapper=$("#wrapper");window.index_on=function(){index_css.insertAfter("meta:eq(0)");result_common_css.remove();result_aladdin_css.remove();result_sug_css.remove();index_content.show();index_foot.show();index_u.show();result_u.hide();wrapper.show();if(bds.su&&bds.su.U&&bds.su.U.homeInit){bds.su.U.homeInit();}setTimeout(function(){try{$('#kw1').get(0).focus();window.sugIndex.start();}catch(e){}},0);if(typeof initIndex=='function'){initIndex();}};window.index_off=function(){index_css.remove();index_content.hide();index_foot.hide();index_u.hide();result_u.show();result_aladdin_css.insertAfter("meta:eq(0)");result_common_css.insertAfter("meta:eq(0)");result_sug_css.insertAfter("meta:eq(0)");wrapper.show();};})();</script>
<script>window.__switch_add_mask=1;</script>
<script type="text/javascript" src="http://s1.bdstatic.com/r/www/cache/static/global/js/instant_search_newi_redirect1_20bf4036.js"></script>
<script>initPreload();$("#u,#u1").delegate("#lb",'click',function(){try{bds.se.login.open();}catch(e){}});if(navigator.cookieEnabled){document.cookie="NOJS=;expires=Sat, 01 Jan 2000 00:00:00 GMT";}</script>
<script>$(function(){for(i=0;i<3;i++){u($($('.s_ipt_wr')[i]),$($('.s_ipt')[i]),$($('.s_btn_wr')[i]),$($('.s_btn')[i]));}function u(iptwr,ipt,btnwr,btn){if(iptwr && ipt){iptwr.on('mouseover',function(){iptwr.addClass('ipthover');}).on('mouseout',function(){iptwr.removeClass('ipthover');}).on('click',function(){ipt.focus();});ipt.on('focus',function(){iptwr.addClass('iptfocus');}).on('blur',function(){iptwr.removeClass('iptfocus');}).on('render',function(e){var $s = iptwr.parent().find('.bdsug');var l = $s.find('li').length;if(l>=5){$s.addClass('bdsugbg');}else{$s.removeClass('bdsugbg');}});}if(btnwr && btn){btnwr.on('mouseover',function(){btn.addClass('btnhover');}).on('mouseout',function(){btn.removeClass('btnhover');});}}});</script>
<script type="text/javascript" src="http://s1.bdstatic.com/r/www/cache/static/home/js/bri_7f1fa703.js"></script>
<script>(function(){var _init=false;window.initIndex=function(){if(_init){return;}_init=true;var w=window,d=document,n=navigator,k=d.f1.wd,a=d.getElementById("nv").getElementsByTagName("a"),isIE=n.userAgent.indexOf("MSIE")!=-1&&!window.opera;(function(){if(/q=([^&]+)/.test(location.search)){k.value=decodeURIComponent(RegExp["\x241"])}})();(function(){var u = G("u1").getElementsByTagName("a"), nv = G("nv").getElementsByTagName("a"), lk = G("lk").getElementsByTagName("a"), un = "";var tj_nv = ["news","tieba","zhidao","mp3","img","video","map"];var tj_lk = ["baike","wenku","hao123","more"];un = bds.comm.user == "" ? "" : bds.comm.user;function _addTJ(obj){addEV(obj, "mousedown", function(e){var e = e || window.event;var target = e.target || e.srcElement;if(target.name){ns_c({'fm':'behs','tab':target.name,'un':encodeURIComponent(un)});}});}for(var i = 0; i < u.length; i++){_addTJ(u[i]);}for(var i = 0; i < nv.length; i++){nv[i].name = 'tj_' + tj_nv[i];}for(var i = 0; i < lk.length; i++){lk[i].name = 'tj_' + tj_lk[i];}})();(function() {var links = {'tj_news': ['word', 'http://news.baidu.com/ns?tn=news&cl=2&rn=20&ct=1&ie=utf-8'],'tj_tieba': ['kw', 'http://tieba.baidu.com/f?ie=utf-8'],'tj_zhidao': ['word', 'http://zhidao.baidu.com/search?pn=0&rn=10&lm=0'],'tj_mp3': ['key', 'http://music.baidu.com/search?fr=ps&ie=utf-8'],'tj_img': ['word', 'http://image.baidu.com/i?ct=201326592&cl=2&nc=1&lm=-1&st=-1&tn=baiduimage&istype=2&fm=&pv=&z=0&ie=utf-8'],'tj_video': ['word', 'http://video.baidu.com/v?ct=301989888&s=25&ie=utf-8'],'tj_map': ['wd', 'http://map.baidu.com/?newmap=1&ie=utf-8&s=s'],'tj_baike': ['word', 'http://baike.baidu.com/search/word?pic=1&sug=1&enc=utf8'],'tj_wenku': ['word', 'http://wenku.baidu.com/search?ie=utf-8']};var domArr = [G('nv'), G('lk'),G('cp')],kw = G('kw1');for (var i = 0, l = domArr.length; i < l; i++) {domArr[i].onmousedown = function(e) {e = e || window.event;var target = e.target || e.srcElement,name = target.getAttribute('name'),items = links[name],reg = new RegExp('^\\s+|\\s+\x24'),key = kw.value.replace(reg, '');if (items) {if (key.length > 0) {var wd = items[0], url = items[1],url = url + ( name === 'tj_map' ? encodeURIComponent('&' + wd + '=' + key) : ( ( url.indexOf('?') > 0 ? '&' : '?' ) + wd + '=' + encodeURIComponent(key) ) );target.href = url;} else {target.href = target.href.match(new RegExp('^http:\/\/.+\.baidu\.com'))[0];}}name && ns_c({'fm': 'behs','tab': name,'query': encodeURIComponent(key),'un': encodeURIComponent(bds.comm.user || '') });};}})();};if(window.pageState==0){initIndex();}})();document.cookie = 'IS_STATIC=1;expires=' + new Date(new Date().getTime() + 10*60*1000).toGMTString();</script>
</body></html>

libcurl入门之http通信(2)——打开log查看libcurl的执行过程

1 摘要
1.1 平台

运行平台:君正x2000
libcurl版本:7.68.0,通过buildroot编译生成。
编译器:mips-linux-gnu-gcc (gcc version 7.2.0 (Ingenic r4.0.0-gcc720 2018.02-28))
编译主机:ubuntu 18.04

1.2 功能
http通信(1)

1.1最少的代码实现http访问百度,这个示例将通过get方法访问百度,并将百度返回的结果存入data.html文件中。
http通信(2)
2.1 增加获取libcurl库信息
2.2 增加打印打印http响应head数据
2.3 增加使能版本库调试功能

2 打印版本库信息

static void print_version_info(void)
{
	curl_version_info_data *version_info = curl_version_info(CURLVERSION_NOW);
 
	printf("version:%s\n", version_info->version);
	printf("host:%s\n", version_info->host);
	printf("ssl_version:%s\n", version_info->ssl_version);
}
 
int main()
{
    ........
    ........
    /*
    * 打印本本库信息
    */
    print_version_info();
	
    ....
    ....
    return 0;
}

2.1 执行结果

# ./test
version:7.68.0
host:mipsel-buildroot-linux-gnu
ssl_version:OpenSSL/1.1.1g

3 增加调试功能
CURLOPT_VERBOSE选项非常有用,当我们在调试嵌入式系统的时候没有wireshark等抓包工具,打开CURLOPT_VERBOSE选项可以看到libcurl的执行过程

/*
 * 设置libcurl的选项,没有指定http访问方法,libcurl默认使用get方法
 */
curl_easy_setopt(easy_handle, CURLOPT_URL, "http://www.baidu.com");
curl_easy_setopt(easy_handle, CURLOPT_WRITEFUNCTION, baidu_get_test_cb);
curl_easy_setopt(easy_handle, CURLOPT_WRITEDATA, fp);
/*
 * 增加调试功能,可以查看libcurl的执行过程
 */
curl_easy_setopt(easy_handle, CURLOPT_VERBOSE, 1);

3.1 执行结果

# ./test
version:7.68.0
host:mipsel-buildroot-linux-gnu
ssl_version:OpenSSL/1.1.1g
*   Trying 110.242.68.3:80...
* TCP_NODELAY set
* Connected to www.baidu.com (110.242.68.3) port 80 (#0)
> GET / HTTP/1.1
Host: www.baidu.com
Accept: */*
 
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Accept-Ranges: bytes
< Cache-Control: no-cache
< Connection: keep-alive
< Content-Length: 14615
< Content-Type: text/html
< Date: Sat, 20 Mar 2021 03:02:49 GMT
< P3p: CP=" OTI DSP COR IVA OUR IND COM "
< P3p: CP=" OTI DSP COR IVA OUR IND COM "
< Pragma: no-cache
< Server: BWS/1.1
< Set-Cookie: BAIDUID=948BD633A8A66E61E08D2D51C124E8F2:FG=1; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com
< Set-Cookie: BIDUPSID=948BD633A8A66E61E08D2D51C124E8F2; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com
< Set-Cookie: PSTM=1616209369; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com
< Set-Cookie: BAIDUID=948BD633A8A66E611382AE5374C312B9:FG=1; max-age=31536000; expires=Sun, 20-Mar-22 03:02:49 GMT; domain=.baidu.com; path=/; version=1; comment=bd
< Traceid: 161620936935453114985383216040678206036
< Vary: Accept-Encoding
< X-Ua-Compatible: IE=Edge,chrome=1
< 
* Connection #0 to host www.baidu.com left intact

4 选项名:CURLOPT_HEADERFUNCTION
CURLOPT_WRITEFUNCTION 用于接收http get返回的数据部分信息,如果想接收http get返回的头部信息,可以使用CURLOPT_HEADERFUNCTION选项

4.1 原型

#include <curl/curl.h>

size_t header_callback(char *buffer,   size_t size,   size_t nitems,   void *userdata);
CURLcode curl_easy_setopt(CURL *handle, CURLOPT_HEADERFUNCTION, header_callback);

4.2 描述
传递一个上面原型中类型的回调函数指针。

当libcurl接收头数据的时候这个函数被调用。每一个头部信息,都会调用一次这个函数并且只有完整的头才会产生回调。使用这个函数去解析头部是非常简单的。buffer指针指向被接被接收的数据。nitems是数据的大小,size总是1。不要认为一行头信息是以’\0’结束的。

userdata指针是你通过CURLOPT_HEADERDATA选项设置的。

回调函数必须返回实际被处理的字节个数,如果回调函数返回的和传进来的字节数不同,libcurl将会产生一个错误信号。这将会引起传输终止,并且执行函数将会返回一个CURLE_WRITE_ERROR错误。

传入到回到函数的完整的http可能多大CURL_MAX_HTTP_HEADER (100K)字节数,这其中也包括最后一行含有的终止符。

重要的是要注意,在发起请求后,将为收到的所有响应的头调用回调函数,而不仅仅是最终响应。这包括在身份验证协商期间发生的所有响应。如果你只需要对最终响应的头进行操作,那么您将需要自己在回调中收集头信息,并使用HTTP状态行来划分响应边界。

对于HTTP传输,状态行和响应主体前面的空行都包含在报头中,并传递给此函数。

5 代码实实现
Makefile

LIBS=-L../../../buildroot/output/target/usr/lib -lpthread -lz -lcurl -lcrypto -lssl -lcjson
CC_FLAG=-Wall -I../../../buildroot/output/staging/usr/include
SRC=curl_test.c
CC=mips-linux-gnu-gcc 
 
 
test : $(SRC)
	$(CC) $< $(LIBS) $(CC_FLAG) -O2 -o $@
	cp -v $@ ~/nfs
 
.PHONY : clean
 
clean :
	rm -rf test

curl_test.c

#include <stdio.h>
#include <curl/curl.h>
#include <string.h>
#include <time.h>
#include <stdlib.h>
 
static void baidu_get_test(void);
static void print_version_info(void);
 
int main()
{
	CURLcode code;
	code = curl_global_init(CURL_GLOBAL_ALL);
	
	if(CURLE_OK != code) 
	{
		printf("curl init Err\n");
		return -1;
	}
	
	/*
	 * 打印本本库信息
	 */
	print_version_info();
	
	/*
	 * get 方法访问百度并获取数据
	 */
	baidu_get_test();
	
	curl_global_cleanup();
 
	return 0;
}
 
 
static size_t baidu_get_test_cb(void *buffer, size_t size, size_t nmemb, void *user_p)
{
	FILE *fp = (FILE *)user_p;
	size_t return_size = fwrite(buffer, size, nmemb, fp);
	return return_size;
}
 
static size_t head_process_data(char *buffer, size_t size, size_t nitems, void *userdata)
{
	char *head_line = userdata;
	
	printf("nitems = %d\n", nitems);
 
	strncpy(head_line, (const char *)buffer, nitems);
	*(head_line + nitems) = 0;
	
	printf("head info : %s", head_line);
 
	return nitems;
 
}
 
static void baidu_get_test(void)
{
	/*
	 * 获取一个easy句柄,后面通过这个句柄进行通信
	 */
	CURL *easy_handle = curl_easy_init();
 
	if(!easy_handle) 
	{
		printf("curl_easy_init Err \n");
		curl_global_cleanup();
		return ;
	}
 
	/*
	 * 打开一个data.html文件,用于存储百度返回的数据
	 */
	FILE *fp = fopen("data.html", "w+");
	char *head_line = (char *)malloc(100*1024);
 
 
	/*
	 * 设置libcurl的选项,没有指定http访问方法,libcurl默认使用get方法
	 */
	curl_easy_setopt(easy_handle, CURLOPT_URL, "http://www.baidu.com");
	curl_easy_setopt(easy_handle, CURLOPT_WRITEFUNCTION, baidu_get_test_cb);
	curl_easy_setopt(easy_handle, CURLOPT_WRITEDATA, fp);
	
 
	/*
	 * 增加调试功能,可以查看libcurl的执行过程
	 */
	curl_easy_setopt(easy_handle, CURLOPT_VERBOSE, 1);
	
	/*
	 * 接收http头信息
	 */
	curl_easy_setopt(easy_handle, CURLOPT_HEADERFUNCTION, head_process_data);
	curl_easy_setopt(easy_handle, CURLOPT_HEADERDATA, head_line);
	 
	/*
	 * 执行访问
	 */
	CURLcode code = curl_easy_perform(easy_handle);
	
	
	if(CURLE_OK != code) 
	{
		printf("curl_easy_perform %d\n", code);
	}
	
	free(head_line);
	
	fclose(fp);
	/*
	 * 最后记得释放句柄
	 */
	curl_easy_cleanup(easy_handle);	
}
 
 
static void print_version_info(void)
{
	curl_version_info_data *version_info = curl_version_info(CURLVERSION_NOW);
 
	printf("version:%s\n", version_info->version);
	printf("host:%s\n", version_info->host);
	printf("ssl_version:%s\n", version_info->ssl_version);
}

libcurl入门之http通信(3)——设置自定义HTTP请求头

1 摘要
1.1 平台

运行平台:君正x2000
libcurl版本:7.68.0,通过buildroot编译生成。
编译器:mips-linux-gnu-gcc (gcc version 7.2.0 (Ingenic r4.0.0-gcc720 2018.02-28))
编译主机:ubuntu 18.04

1.2 功能

http通信(1)
1.1最少的代码实现http访问百度,这个示例将通过get方法访问百度,并将百度返回的结果存入data.html文件中。
http通信(2)
2.1 增加获取libcurl库信息
2.2 增加打印打印http响应head数据
2.3 增加使能版本库调试功能
http通信(3)
3.1 在http请求中增加头部的字段

2 选项名:CURLOPT_HTTPHEADER
设置自定义的HTTP头信息

2.1 原型

#include <curl/curl.h>

CURLcode curl_easy_setopt(CURL *handle, CURLOPT_HTTPHEADER, struct curl_slist *headers);

2.2 描述
传入一个指向链表的指针,这个链表包含了http通信的头部字段。

链表应该是一个填充了 struct curl_slist结构的全部有效的链表。使用curl_slist_append创建一个链表,使用释放是一个链表curl_slist_free_all 。如果链表中添加了一个头部字段,而这个头部字段在libcurl内部使用了,那么头部字段的内容默认会使用你设置的内容。如果你添加了一个没有内容的头部,比如’Accept:'(冒号右边没有数据),那么内部使用的这个头部字段将被禁止。这个选项CURLOPT_HTTPHEADER可以用于添加新的头部字段或者替代一个内部头部字段或者删除一个内部头部字段。如果要添加一个没有内容的字段(冒号的右边没有值),那么使用分号结尾的头部字段(muhead;) 。

链表内的每一个头部必须不要添加CRLF(“\r\n”) ,因为libcurl会自动的为每一个头部字段添加CRLF 。如果不遵守这个规则,将会导致奇怪的bug,因为服务器很可能会忽略你指定的一部分头部字段。

请求的第一行(包含方法,通常是GET或POST)不是头部字段,不能使用此选项替换。只有请求行后面的行才是头部字段。如果想改变方法行(第一行)可以使用CURLOPT_CUSTOMREQUEST选项。

传给curl_easy_setopt的这个链表,libcurl并不会拷贝,所以在请求执行完成之前必须保持这个链表不被释放,请求执行完以后,不再使用这个链表了,可以通过curl_slist_free_all释放。

将NULL传递给该选项,可以将其重置为无自定义头。

2.3 示例代码

CURL *curl = curl_easy_init();
 
struct curl_slist *list = NULL;
 
if(curl) 
{
  curl_easy_setopt(curl, CURLOPT_URL, "https://example.com");
 
  list = curl_slist_append(list, "Shoesize: 10");
  list = curl_slist_append(list, "Accept:");
 
  curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list);
 
  curl_easy_perform(curl);
 
  curl_slist_free_all(list); /* free the list again */
}

3 功能代码

默认

GET / HTTP/1.1
Host: www.baidu.com
Accept: */*

修改后

GET / HTTP/1.1
name: huohongpeng
Content-Type:
Accept: application/json

Makefile

LIBS=-L../../../buildroot/output/target/usr/lib -lpthread -lz -lcurl -lcrypto -lssl -lcjson
CC_FLAG=-Wall -I../../../buildroot/output/staging/usr/include
SRC=curl_test.c
CC=mips-linux-gnu-gcc 
 
 
test : $(SRC)
	$(CC) $< $(LIBS) $(CC_FLAG) -O2 -o $@
	cp -v $@ ~/nfs
 
.PHONY : clean
 
clean :
	rm -rf test

curl_test.c

#include <stdio.h>
#include <curl/curl.h>
#include <string.h>
#include <time.h>
#include <stdlib.h>
 
static void baidu_get_test(void);
static void print_version_info(void);
 
int main()
{
	CURLcode code;
	code = curl_global_init(CURL_GLOBAL_ALL);
	
	if(CURLE_OK != code) 
	{
		printf("curl init Err\n");
		return -1;
	}
	
	/*
	 * 打印本本库信息
	 */
	print_version_info();
	
	/*
	 * get 方法访问百度并获取数据
	 */
	baidu_get_test();
	
	curl_global_cleanup();
 
	return 0;
}
 
 
static size_t baidu_get_test_cb(void *buffer, size_t size, size_t nmemb, void *user_p)
{
	FILE *fp = (FILE *)user_p;
	size_t return_size = fwrite(buffer, size, nmemb, fp);
	return return_size;
}
 
static size_t head_process_data(char *buffer, size_t size, size_t nitems, void *userdata)
{
	char *head_line = userdata;
	
	printf("nitems = %d\n", nitems);
 
	strncpy(head_line, (const char *)buffer, nitems);
	*(head_line + nitems) = 0;
	
	printf("head info : %s", head_line);
 
	return nitems;
 
}
 
static void baidu_get_test(void)
{
	/*
	 * 获取一个easy句柄,后面通过这个句柄进行通信
	 */
	CURL *easy_handle = curl_easy_init();
 
	if(!easy_handle) {
		printf("curl_easy_init Err \n");
		curl_global_cleanup();
		return ;
	}
 
	/*
	 * 打开一个data.html文件,用于存储百度返回的数据
	 */
	FILE *fp = fopen("data.html", "w+");
	char *head_line = (char *)malloc(100*1024);
 
 
	/*
	 * 设置libcurl的选项,没有指定http访问方法,libcurl默认使用get方法
	 */
	curl_easy_setopt(easy_handle, CURLOPT_URL, "http://www.baidu.com");
	curl_easy_setopt(easy_handle, CURLOPT_WRITEFUNCTION, baidu_get_test_cb);
	curl_easy_setopt(easy_handle, CURLOPT_WRITEDATA, fp);
	
 
	/*
	 * 增加调试功能,可以查看libcurl的执行过程
	 */
	curl_easy_setopt(easy_handle, CURLOPT_VERBOSE, 1);
	
	/*
	 * 接收http头信息
	 */
	curl_easy_setopt(easy_handle, CURLOPT_HEADERFUNCTION, head_process_data);
	curl_easy_setopt(easy_handle, CURLOPT_HEADERDATA, head_line);
	 
	struct curl_slist *plist;
	plist = curl_slist_append(0, "name: huohongpeng");
	/*
	 * 删除Host字段
	 */
	curl_slist_append(plist, "Host:");
	/*
	 * 添加没有内容的字段
	 */
	curl_slist_append(plist, "Content-Type;");
	/*
	 * 修改Accept: application/json
	 */
	curl_slist_append(plist, "Accept: application/json");
	curl_easy_setopt(easy_handle, CURLOPT_HTTPHEADER, plist);
 
	/*
	 * 执行访问
	 */
	CURLcode code = curl_easy_perform(easy_handle);
	
	
	if(CURLE_OK != code) 
	{
		printf("curl_easy_perform %d\n", code);
	}
	
	free(head_line);
	
	fclose(fp);
	curl_slist_free_all(plist);
	/*
	 * 最后记得释放句柄
	 */
	curl_easy_cleanup(easy_handle);	
}
 
 
static void print_version_info(void)
{
	curl_version_info_data *version_info = curl_version_info(CURLVERSION_NOW);
 
	printf("version:%s\n", version_info->version);
	printf("host:%s\n", version_info->host);
	printf("ssl_version:%s\n", version_info->ssl_version);
}

libcurl入门之http通信(4)——接收前一次传输的总时间、获取下载的字节数

1 摘要
1.1 平台

运行平台:君正x2000
libcurl版本:7.68.0,通过buildroot编译生成。
编译器:mips-linux-gnu-gcc (gcc version 7.2.0 (Ingenic r4.0.0-gcc720 2018.02-28))
编译主机:ubuntu 18.04

1.2 功能
http通信(1)
1.1最少的代码实现http访问百度,这个示例将通过get方法访问百度,并将百度返回的结果存入data.html文件中。
http通信(2)
2.1 增加获取libcurl库信息
2.2 增加打印打印http响应head数据
2.3 增加使能版本库调试功能
http通信(3)
3.1 在http请求中增加头部的字段
http通信(4)
4.1 获取libcurl执行过程信息

2 信息名称:CURLINFO_TOTAL_TIME
获取上一次的总传输时间

2.1 原型

#include <curl/curl.h>

CURLcode curl_easy_getinfo(CURL *handle, CURLINFO_TOTAL_TIME, double *timep);

2.2 描述
传递一个指向double的指针来接收前一次传输的总时间(以秒为单位),包括名称解析、TCP连接等,double表示以秒为单位的时间。

当执行重定向时,每个请求的时间被加在一起。

2.3 示例代码

curl = curl_easy_init();
if(curl) 
{
  double total;
  curl_easy_setopt(curl, CURLOPT_URL, url);
  res = curl_easy_perform(curl);
  if(CURLE_OK == res) 
  {
    res = curl_easy_getinfo(curl, CURLINFO_TOTAL_TIME, &total);
    if(CURLE_OK == res) 
    {
      printf("Time: %.1f", total);
    }
  }
  /* always cleanup */
  curl_easy_cleanup(curl);
}

3 信息名称:CURLINFO_CONNECT_TIME
获取连接过程的时间

3.1 原型

#include <curl/curl.h>

CURLcode curl_easy_getinfo(CURL *handle, CURLINFO_CONNECT_TIME, double *timep);

3.2 描述
传递一个指向double的指针来接收从开始到远程主机(或代理)连接完成的总时间(以秒为单位)。

当执行重定向时,每个请求的时间被加在一起。

3.3 示例代码

curl = curl_easy_init();
if(curl) 
{
  double connect;
  curl_easy_setopt(curl, CURLOPT_URL, url);
  res = curl_easy_perform(curl);
  if(CURLE_OK == res) 
  {
    res = curl_easy_getinfo(curl, CURLINFO_CONNECT_TIME, &connect);
    if(CURLE_OK == res) 
    {
      printf("Time: %.1f", connect);
    }
  }
  /* always cleanup */
  curl_easy_cleanup(curl);
}

4 信息名称:CURLINFO_SIZE_DOWNLOAD

获取下载的字节数。

4.1 原型

#include <curl/curl.h>

CURLcode curl_easy_getinfo(CURL *handle, CURLINFO_SIZE_DOWNLOAD, double *dlp);

4.2 描述
传递一个指向double的指针来接收下载的字节总数。这个数据只针对最后一次传输,每一次传输都会重新计算这个数。这是计算实际有效载荷数据,通常也被称为body。所有元数据和头部数据将不被计算在内。

4.3 示例

CURL *curl = curl_easy_init();
if(curl) 
{
  curl_easy_setopt(curl, CURLOPT_URL, "https://example.com");
 
  /* Perform the request */
  res = curl_easy_perform(curl);
 
  if(!res) 
  {
    /* check the size */
    double dl;
    res = curl_easy_getinfo(curl, CURLINFO_SIZE_DOWNLOAD, &dl);
    if(!res) 
    {
      printf("Downloaded %.0f bytes\n", cl);
    }
  }

5 代码实现
Makefile

LIBS=-L../../../buildroot/output/target/usr/lib -lpthread -lz -lcurl -lcrypto -lssl -lcjson
CC_FLAG=-Wall -I../../../buildroot/output/staging/usr/include
SRC=curl_test.c
CC=mips-linux-gnu-gcc 
 
 
test : $(SRC)
	$(CC) $< $(LIBS) $(CC_FLAG) -O2 -o $@
	cp -v $@ ~/nfs
 
.PHONY : clean
 
clean :
	rm -rf test

curl_test.c

#include <stdio.h>
#include <curl/curl.h>
#include <string.h>
#include <time.h>
#include <stdlib.h>
 
static void baidu_get_test(void);
static void print_version_info(void);
 
int main()
{
	CURLcode code;
	code = curl_global_init(CURL_GLOBAL_ALL);
	
	if(CURLE_OK != code) 
	{
		printf("curl init Err\n");
		return -1;
	}
	
	/*
	 * 打印本本库信息
	 */
	print_version_info();
	
	/*
	 * get 方法访问百度并获取数据
	 */
	baidu_get_test();
	
	curl_global_cleanup();
 
	return 0;
}
 
 
static size_t baidu_get_test_cb(void *buffer, size_t size, size_t nmemb, void *user_p)
{
	FILE *fp = (FILE *)user_p;
	size_t return_size = fwrite(buffer, size, nmemb, fp);
	return return_size;
}
 
static size_t head_process_data(char *buffer, size_t size, size_t nitems, void *userdata)
{
	char *head_line = userdata;
	
	printf("nitems = %d\n", nitems);
 
	strncpy(head_line, (const char *)buffer, nitems);
	*(head_line + nitems) = 0;
	
	printf("head info : %s", head_line);
 
	return nitems;
}
 
 
static void baidu_get_test(void)
{
	/*
	 * 获取一个easy句柄,后面通过这个句柄进行通信
	 */
	CURL *easy_handle = curl_easy_init();
 
	if(!easy_handle) 
	{
		printf("curl_easy_init Err \n");
		curl_global_cleanup();
		return ;
	}
 
	/*
	 * 打开一个data.html文件,用于存储百度返回的数据
	 */
	FILE *fp = fopen("data.html", "w+");
	char *head_line = (char *)malloc(100*1024);
 
 
	/*
	 * 设置libcurl的选项,没有指定http访问方法,libcurl默认使用get方法
	 */
	curl_easy_setopt(easy_handle, CURLOPT_URL, "http://www.baidu.com");
	curl_easy_setopt(easy_handle, CURLOPT_WRITEFUNCTION, baidu_get_test_cb);
	curl_easy_setopt(easy_handle, CURLOPT_WRITEDATA, fp);
	
	/*
	 * 增加调试功能,可以查看libcurl的执行过程
	 */
	curl_easy_setopt(easy_handle, CURLOPT_VERBOSE, 1);
	
	/*
	 * 接收http头信息
	 */
	curl_easy_setopt(easy_handle, CURLOPT_HEADERFUNCTION, head_process_data);
	curl_easy_setopt(easy_handle, CURLOPT_HEADERDATA, head_line);
 
 
	struct curl_slist *plist;
	plist = curl_slist_append(0, "name: huohongpeng");
	/*
	 * 删除Host字段
	 */
	curl_slist_append(plist, "Host:");
	/*
	 * 添加没有内容的字段
	 */
	curl_slist_append(plist, "Content-Type;");
	/*
	 * 修改Accept: application/json
	 */
	curl_slist_append(plist, "Accept: application/json");
	curl_easy_setopt(easy_handle, CURLOPT_HTTPHEADER, plist);
 
	/*
	 * 执行访问
	 */
	CURLcode code = curl_easy_perform(easy_handle);
	
	if(CURLE_OK != code) 
	{
		printf("curl_easy_perform %d\n", code);
	}
	
	/*
	 * 获取信息
	 */
	double tmp = 0;
	code = curl_easy_getinfo(easy_handle, CURLINFO_TOTAL_TIME, &tmp);
	printf("CURLINFO_TOTAL_TIME = %lf\n", tmp);
 
	curl_easy_getinfo(easy_handle, CURLINFO_CONNECT_TIME, &tmp);
	printf("CURLINFO_CONNECT_TIME = %lf\n", tmp);
	
	curl_easy_getinfo(easy_handle, CURLINFO_SIZE_DOWNLOAD, &tmp);
	printf("CURLINFO_SIZE_DOWNLOAD = %d\n", (int)tmp);
	
	
	free(head_line);
	
	fclose(fp);
	curl_slist_free_all(plist);
	/*
	 * 最后记得释放句柄
	 */
	curl_easy_cleanup(easy_handle);	
}
 
 
static void print_version_info(void)
{
	curl_version_info_data *version_info = curl_version_info(CURLVERSION_NOW);
 
	printf("version:%s\n", version_info->version);
	printf("host:%s\n", version_info->host);
	printf("ssl_version:%s\n", version_info->ssl_version);
}
  • 17
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: libcurl是一个开源的网络传输库,它提供了丰富的API,可以用于发送和接收各种协议的数据。libcurl编程手册详细介绍了库的各种功能、使用方法和示例代码,让开发者能够更好地理解和使用这个库。 编程手册包含了libcurl库的详细介绍,包括库的基本概念、常用函数、选项以及回调函数等。手册详细阐述了每个API函数的功能和参数,可以帮助开发者正确地调用这些函数。此外,手册还解释了一些高级的特性,比如多线程操作、进度回调和SSL/TLS支持等,帮助开发者更深入地了解和利用libcurl的功能。 手册中还提供了一些代码示例,这些示例涵盖了各种常见场景,比如发送GET请求、发送POST请求、上传和下载文件、处理Cookie等。开发者可以根据需要选择合适的示例代码,或根据示例代码进行修改和扩展,以满足自己的具体需求。示例代码清晰地展示了如何使用libcurl库的各种功能和API,可以作为开发者学习和参考的范本。 总之,libcurl编程手册及代码示例是一个非常有价值的资源,它帮助开发者快速入门和熟悉libcurl库的使用方法,提高网络传输相关的开发工作效率。通过阅读手册和实践示例,开发者可以轻松地在自己的项目中使用libcurl库,实现各种网络传输的需求。 ### 回答2: libcurl是一款开源的网络传输库,旨在帮助开发者实现各种网络通信功能。它提供了简单的API接口,支持多种协议(如HTTP、FTP、SMTP等),可以在各种操作系统上运行。libcurl编程手册包含了详细的文档,以帮助开发者理解库的使用方法和功能。 编程手册首先介绍了libcurl的基本概念和术语,然后详细解释了API接口的使用,包括初始化和清理、发送请求、响应处理等。手册还提供了高级功能的说明,如多线程支持、SSL安全连接、数据传输控制等。此外,手册还包含了一些实例代码,以帮助开发者更好地理解库的使用。 下面是一个简单的libcurl代码实例,用于向指定的网页发送GET请求,并将响应内容输出到控制台: ``` #include <stdio.h> #include <curl/curl.h> // 回调函数,用于处理响应内容 size_t write_callback(void *buffer, size_t size, size_t nmemb, void *userp) { printf("%.*s", size * nmemb, (char *)buffer); return size * nmemb; } int main(void) { CURL *curl; CURLcode res; curl = curl_easy_init(); if(curl) { curl_easy_setopt(curl, CURLOPT_URL, "https://www.example.com"); // 设置回调函数 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback); // 执行请求 res = curl_easy_perform(curl); if(res != CURLE_OK) fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); // 清理资源 curl_easy_cleanup(curl); } return 0; } ``` 以上代码通过curl_easy_init()初始化一个CURL对象,并使用curl_easy_setopt()设置URL和回调函数。最后,通过curl_easy_perform()执行请求,并使用curl_easy_cleanup()清理资源。在响应处理的回调函数中,使用printf()将响应内容输出到控制台。 通过编程手册的学习和实例代码的实践,开发者可以更好地理解libcurl的使用方法和功能,从而实现各种网络通信需求。 ### 回答3: libcurl是一个强大、灵活的开源网络传输库,它支持多种协议,包括HTTP、HTTPS、FTP、SMTP等。libcurl编程手册是官方提供的文档,它详细介绍了libcurl的使用方法和API接口,帮助开发者更好地使用libcurl进行网络传输操作。 libcurl编程手册的主要内容包括库的初始化、URL传输、HTTP传输、FTP传输、SSL、回调函数和错误处理等。在手册中,开发者可以了解到如何初始化libcurl库,并进行必要的配置和选项设置。手册介绍了以同步和异步方式进行传输数据的方法,并提供了详细的代码示例,方便开发者学习和实践。 手册还涵盖了HTTP传输相关的内容,包括GET和POST请求的发送、处理http头部、Cookie传输和重定向等。对于FTP传输,手册介绍了文件上传和下载的方法,并提供了相应的代码示例。 此外,手册还详细解释了如何通过libcurl进行SSL/TLS传输,以便进行安全的通信。开发者可以了解到如何验证服务器证书和进行双向认证等相关知识。 回调函数是libcurl中一个重要的概念,手册提供了回调函数的使用方法和实践指南。开发者可以根据实际需求,编写回调函数来处理libcurl传输过程中的事件和数据。 在手册中,还有对常见错误的处理方法的介绍,使开发者能够更好地处理和调试libcurl的错误。 总结来说,libcurl编程手册提供了全面、详细的关于libcurl的介绍和使用方法,通过手册中的代码示例,开发者可以快速了解和掌握libcurl的使用技巧。对于需要进行网络数据传输的开发者来说,libcurl是一个强大的工具,而libcurl编程手册则是学习和实践的重要参考。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

行稳方能走远

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值