Linux下的http请求有许多种方式,其中curl库是C语言封装的一个强大的库,使用curl比封装socket更加方便。

8.1 Curl使用

Curl是一款著名的字符界面下的下载工具,支持HTTP、HTTPS、FTP、FTPS、DICT、TELNET、LDAP、FILE,和GOPHER。此外还具有cookies支持、断点续传、FTP上传、密码支持、SSL支持和代理支持等特性。curl同时还提供了一套libcurl的库,开发者可以基于这个库开发其他下载工具。

8.1.1 Curl安装

8.1.1.1 Ubuntu 上安装

Curl 的官网下载地址:http://curl.haxx.se/download/

截止2018.4月更新至7.59.0版本。

1.下载并解压

下载到的压缩包为curl-7.59.0.tar.gz使用命令:

$wget  http://curl.haxx.se/download/curl-7.59.0.tar.gz

解压:

$tar -zxvf curl-7.59.0.tar.gz

2.进入解压出的目录curl-7.59.0.

$cd curl-7.59.0

3.配置参数

sudo ./configure

4.编译

sudo make  
sudo make install

5.检查安装

使用 curl --version 检查是否更新成功,出现如下情况安装基本成功.

$curl --version

《Linux操作系统 - 高级编程》第三部分 网络编程 (8 Linux下实现C语言的HTTP请求实现)_curl

出现上图所示信息表示安装成功。

8.1.1.2 ARM板上安装

1.下载并解压

下载到的压缩包为curl-7.59.0.tar.gz使用命令:

$wget  http://curl.haxx.se/download/curl-7.59.0.tar.gz

解压:

$tar -zxvf curl-7.59.0.tar.gz

2.进入解压出的目录curl-7.59.0.

$cd curl-7.59.0

3.建立编译夹

$mkdir install

4.配置参数

./configure --host=arm-fsl-linux-gnueabi ­­prefix=`pwd`/install CC=arm-fsl-linux-gnueabi-gcc CXX=arm-fsl-linux-gnueabi-g++

【注意】

–­­host=arm-fsl-linux-gnueabi表示该软件编译完成后在arm平台上运行

–­­prefix后面为软件安装目录

5.编译

$make
$make install

6.查看

$ls install/

《Linux操作系统 - 高级编程》第三部分 网络编程 (8 Linux下实现C语言的HTTP请求实现)_数据_02

libcurl头文件在install/include/curl目录

# ls install/include/curl

《Linux操作系统 - 高级编程》第三部分 网络编程 (8 Linux下实现C语言的HTTP请求实现)_linux_03

交叉编译后的动态库文件在lib目录

$ls install/lib

《Linux操作系统 - 高级编程》第三部分 网络编程 (8 Linux下实现C语言的HTTP请求实现)_curl_04

7.移植

将lib文件下的移植到开发板的/usr/lib。

8.测试

Test.c

#include <stdio.h>  
#include <string.h>
#include <curl/curl.h>

const char data[]="this is what we post to the silly web server";

struct WriteThis
{
const char *readptr;
long sizeleft;
};

static size_t read_callback(void *ptr, size_t size, size_t nmemb, void *userp)
{
struct WriteThis *pooh = (struct WriteThis *)userp;

if(size*nmemb < 1)
return 0;

if(pooh->sizeleft)
{
*(char *)ptr = pooh->readptr[0]; /* copy one single byte */
pooh->readptr++; /* advance pointer */
pooh->sizeleft--; /* less data left */
return 1; /* we return 1 byte at a time! */
}

return 0; /* no more data left to deliver */
}

int main(void)
{
CURL *curl;
CURLcode res;

struct WriteThis pooh;

pooh.readptr = data;
pooh.sizeleft = (long)strlen(data);

/* In windows, this will init the winsock stuff */
res = curl_global_init(CURL_GLOBAL_DEFAULT);
/* Check for errors */
if(res != CURLE_OK)
{
fprintf(stderr, "curl_global_init() failed: %s\n",
curl_easy_strerror(res));
return 1;
}

/* get a curl handle */
curl = curl_easy_init();
if(curl)
{
/* First set the URL that is about to receive our POST. */
curl_easy_setopt(curl, CURLOPT_URL, "http://47.106.72.113/");

/* Now specify we want to POST data */
curl_easy_setopt(curl, CURLOPT_POST, 1L);

/* we want to use our own read function */
curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_callback);

/* pointer to pass to our read function */
curl_easy_setopt(curl, CURLOPT_READDATA, &pooh);

/* get verbose debug output please */
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);

/*
If you use POST to a HTTP 1.1 server, you can send data without knowing
the size before starting the POST if you use chunked encoding. You
enable this by adding a header like "Transfer-Encoding: chunked" with
CURLOPT_HTTPHEADER. With HTTP 1.0 or without chunked transfer, you must
specify the size in the request.
*/
#ifdef USE_CHUNKED
{
struct curl_slist *chunk = NULL;

chunk = curl_slist_append(chunk, "Transfer-Encoding: chunked");
res = curl_easy_setopt(curl, CURLOPT_HTTPHEADER, chunk);
/* use curl_slist_free_all() after the *perform() call to free this
list again */
}
#else
/* Set the expected POST size. If you want to POST large amounts of data,
consider CURLOPT_POSTFIELDSIZE_LARGE */
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, pooh.sizeleft);
#endif

#ifdef DISABLE_EXPECT
/*
Using POST with HTTP 1.1 implies the use of a "Expect: 100-continue"
header. You can disable this header with CURLOPT_HTTPHEADER as usual.
NOTE: if you want chunked transfer too, you need to combine these two
since you can only set one list of headers with CURLOPT_HTTPHEADER. */

/* A less good option would be to enforce HTTP 1.0, but that might also
have other implications. */
{
struct curl_slist *chunk = NULL;

chunk = curl_slist_append(chunk, "Expect:");
res = curl_easy_setopt(curl, CURLOPT_HTTPHEADER, chunk);
/* use curl_slist_free_all() after the *perform() call to free this
list again */
}
#endif

/* Perform the request, res will get the return code */
res = curl_easy_perform(curl);
/* Check for errors */
if(res != CURLE_OK)
fprintf(stderr, "curl_easy_perform() failed: %s\n",
curl_easy_strerror(res));

/* always cleanup */
curl_easy_cleanup(curl);
}
curl_global_cleanup();
return 0;
}

执行命令:

$arm-fsl-linux-gnueabi-gcc -I/home/farsight/curl-7.59.0/install/include -L/home/farsight/curl-7.59.0/install/lib  -o test test.c -lcurl

将可执行文件test拷贝到开发板,执行程序。可以见到如下信息。

*   Trying 47.106.72.113...
* TCP_NODELAY set
* Connected to 47.106.72.113 (47.106.72.113) port 80 (#0)
> POST / HTTP/1.1
Host: 47.106.72.113
Accept: */*
Content-Length: 44
Content-Type: application/x-www-form-urlencoded

* We are completely uploaded and fine
< HTTP/1.1 405 Not Allowed
< Server: nginx/1.8.1
< Date: Mon, 25 Jun 2018 05:45:38 GMT
< Content-Type: text/html
< Content-Length: 172
< Connection: keep-alive
<
<html>
<head><title>405 Not Allowed</title></head>
<body bgcolor="white">
<center><h1>405 Not Allowed</h1></center>
<hr><center>nginx/1.8.1</center>
</body>
</html>
* Connection #0 to host 47.106.72.113 left intact

libcurl测试实例集:https://curl.haxx.se/libcurl/c/example.html

关于libcurl实例参看以上网址。

8.1.2 Curl使用步骤

1.全局初始化

CURLcode curl_global_init(long flags);
flags:
CURL_GLOBAL_ALL //初始化所有内部的模块
CURL_GLOBAL_SSL //初始化支持安全套接字
CURL_GLOBAL_WIN32 //初始化win32套接字库
CURL_GLOBAL_NOTHING //不初始化任何额外模块
CURL_GLOBAL_DEFAULT //与CURL_GLOBAL_ALL相同

该函数必须再所有其他libcurl函数调用前被调用,从而构建整个libcurl函数运行所需的环境,多次调用是幂等的。flags参数能够使用”或”操作符进行多个选项的拼接。一般情况使用CURL_GLOBAL_ALL是最好的选择。

该函数不是线程安全的,不能在程序的其他线程中调用,只能应用程序开始时,进行全局初始化调用。虽然不调用这个函数,使用curl_easy_init也会自行调用该函数,但在多线程处理时,可能出现多次调用的情况,应避免。

2.创建当次请求句柄

每次请求都需要创建一个句柄,所有操作围绕以此句柄进行:

CURL *curl_handler = curl_easy_init();

3.设置属性

libcurl针对所有协议,统一使用一个简单的函数curl_easy_setopt进行设置,这大大简化了使用,不过需要参考不同协议设置不同的属性:

​ https://curl.haxx.se/libcurl/c/curl_easy_setopt.html​

这里以http发送GET请求为例设置如下:

//设置请求的url
curl_easy_setopt(curl_handler, CURLOPT_URL, url);
//设置是否返回请求头
curl_easy_setopt(curl_handler, CURLOPT_HEADER, 1L);
//设置屏蔽其他信号
curl_easy_setopt(curl_handler, CURLOPT_NOSIGNAL, 1L);
//设置下载数据回调函数
curl_easy_setopt(curl_handler, CURLOPT_WRITEFUNCTION, write_func);
curl_easy_setopt(curl_handler, CURLOPT_WRITEDATA, write_data);

回调函数原型:

size_t function( void *ptr, size_t size, size_t nmemb, void *userp);

函数将在libcurl接收到数据后被调用。

void *ptr是下载回来的数据.

void *userp是用户指针, 用户通过这个指针传输自己的数据.

CURLOPT_WRITEDATA:设置回调函数中的void *userp指针的来源

//设置是否使用下载进度控制函数
curl_easy_setopt(curl_handler, CURLOPT_NOPROGRESS, 0L);
curl_easy_setopt(curl_handler, CURLOPT_PROGRESSFUNCTION, prog_func);
curl_easy_setopt(curl_handler, CURLOPT_PROGRESSDATA, pdata);

下载进度回调函数与下载数据的回调函数原型相同,data也相同。

//设置请求头
struct curl_list *header_list = NULL;
for (int i = 0; i < headers.size(); ++i) {
header_list = curl_slist_append(header_list, headers[i].c_str());
}
curl_easy_setopt(curl_handler, CURLOPT_HTTPHEADER, header_list);
curl_slist_free_all(header_list);

//其他选项

CURLOPT_HEADERFUNCTION

CURLOPT_HEADERDATA

只取HTTP头部数据, 处理与下载数据回调的处理相同.

CURLOPT_TIMEOUT

超时时间.

CURLOPT_CONNECTIONTIMEOUT

连接等待时间.

CURLOPT_FOLLOWLOCATION

   设置支持302重定向

   CURLOPT_RANGE

  断点续传, 指定传输分片, 格式:“0-200”

其中,针对http协议,不同请求有特定的属性需要设置,下面依次列举:


  • HEAD:需要设置CURLOPT_NOBODY为true
  • DELETE:需要设置CURLOPT_CUSTOMREQUEST为”DELETE”
  • PUT:需要设置CURLOPT_UPLOAD为true,同时设置CURLOPT_READFUNCTION、CURLOPT_READDATA、CURLOPT_INFILESIZE_LARGE
  • POST:需要设置CURLOPT_POST为true,设置CURLOPT_POSTFIELDS、CURLOPT_POSTFIELDSIZE

4.执行

CURLcode curl_easy_perform(CURL *handler);

该函数执行当次请求创建的句柄,返回值有非常详细的定义libcurl库返回状态码解释与速查。

5.销毁当次请求句柄

void curl_easy_cleanup(CURL *handler);

该函数销毁当次请求创建的句柄。

6.全局析构

void curl_global_cleanup();

该函数销毁全局执行环境。

 其他接口

libcurl除了上述常用接口外,提供了其他接口可以进行更为方便和精确的控制。

CURLcode curl_easy_getinfo(CURL *curl, CURLINFO info, ... );
//支持的CURLINFO类型见此:https://curl.haxx.se/libcurl/c/curl_easy_getinfo.html
curl_version() returns a pointer to the libcurl version string
curl_getdate() converts a date string to time_t
curl_formadd() build multipart form-data posts
curl_formfree() free a previously built form POST
curl_slist_append() builds a linked list
curl_slist_free_all() frees a whole curl_slist as made with curl_slist_append()
curl_easy_escape() URL encodes a string
curl_easy_unescape() URL decodes a string

libcurl官方教程:https://curl.haxx.se/libcurl/c/libcurl-tutorial.html

欢迎访问我的网站:

​BruceOu的哔哩哔哩​

​BruceOu的主页​

​BruceOu的博客​

接收更多精彩文章及资源推送,请订阅我的微信公众号:

《Linux操作系统 - 高级编程》第三部分 网络编程 (8 Linux下实现C语言的HTTP请求实现)_curl_05