第三部分 网络编程 (8 Linux下实现C语言的HTTP请求实现)

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

这里写图片描述
出现上图所示信息表示安装成功。

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/

这里写图片描述
libcurl头文件在install/include/curl目录

# ls install/include/curl

这里写图片描述
交叉编译后的动态库文件在lib目录

$ls install/lib

这里写图片描述
7.移植
将lib文件下的移植到开发板的/usr/lib。

8.测试
Test.c

#include   
#include   
#include   

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 Allowedtitle>head>
<body bgcolor="white">
<center><h1>405 Not Allowedh1>center>
<hr><center>nginx/1.8.1center>
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

你可能感兴趣的:(第三部分 网络编程 (8 Linux下实现C语言的HTTP请求实现))