curl使用

  • 时间:2025-12-01 22:01 作者: 来源: 阅读:3
  • 扫一扫,手机访问
摘要:简介 curl https://github.com/curl/curl libcurl/ https://curl.haxx.se/download.html https://curl.haxx.se/libcurl/c/example.html libcurl作为是一个多协议的便于客户端使用的URL传输库,基于C语言,提供C语言的API接口,支持DICT, FILE, FTP, F

简介

curl

https://github.com/curl/curl

libcurl/

https://curl.haxx.se/download.html

https://curl.haxx.se/libcurl/c/example.html

libcurl作为是一个多协议的便于客户端使用的URL传输库,基于C语言,提供C语言的API接口,支持DICT, FILE, FTP, FTPS, Gopher, HTTP, HTTPS, IMAP, IMAPS, LDAP, LDAPS, POP3, POP3S, RTMP, RTSP, SCP, SFTP, SMTP, SMTPS, Telnet and TFTP这些协议,同时支持使用SSL证书的安全文件传输: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等。

cURL项目还以一种机器可读的格式提供了最新版本的相关信息,URL为 https://curl.haxx.se/info

下载

https://curl.haxx.se/download/curl-7.60.0.tar.gz

https://www.gitbook.com/download/pdf/book/bagder/everything-curl

编译

window

项目支持 Visual C++ v6.0 to v15.0

项目sln文件在 curl-7.60.0projects目录

依赖第三方库: OpenSSL, wolfSSL and SSH2 需要另外下载编译

目录结构


somedirectory
    |_curl
    | |_ build
    |    |_<architecture>
    |      |_<ide>
    |        |_<configuration>
    |          |_lib
    |          |_src
    |
    |_openssl
    | |_ build
    |    |_<architecture>
    |      |_VC <version>
    |        |_<configuration>
    |
    |_libssh2
      |_ build
         |_<architecture>
           |_VC <version>
             |_<configuration>

执行curl/projects文件下generate.bat,生成一个windows文件

或者命令行打开winbuild文件夹:nmake /f makefile.vc mode=static VC=12 ENABLE_IDN=no debug=no

在 Linux 和类 Unix 系统上

在 Linux 和其他类 Unix 系统上构建 curl 的方式有两种。一种是使用配置脚本,另一种是使
用 CMake。

在源代码目录中运行 ./configure

wolfssl

https://github.com/wolfSSL/wolfssl

运行以下命令查看帮助


build-wolfssl -help

OpenSSL

运行以下命令查看帮助


build-openssl -help

OpenSSL需要ActivePerl

build-openssl.bat支持openssl-1.0.2


build-openssl.bat vc10 x86 release

流程

主要函数

https://curl.haxx.se/libcurl/c/

curl_global_init 初始化libcrl


CURLcode curl_global_init(long flags);

flags参数取值范围


CURL_GLOBAL_ALL	初始化所有可能的调用
CURL_GLOBAL_SSL	初始化支持安全套接字层的调用
CURL_GLOBAL_WIN32	初始化WIN32套接字库
CURL_GLOBAL_NOTHING	没有额外的初始化要求

虽然 libcurl是线程安全的,但 curl_global_init是不能保证线程安全的,所以不要在每个线程中都调用 curl_global_init,应该将该函数的调用放在主线程中。

最后调用curl_global_cleanup();

curl_easy_init() 获取CURL操作符


CURL *curl_easy_init( );

初始化一个 CURL的指针(有些像返回 FILE类型的指针一样). 相应的在调用结束时要用 curl_easy_cleanup函数清理.

curl_easy_setopt() 设置传输选项


CURLcode curl_easy_setopt(CURL *handle, CURLoption option, parameter);

可指定 libcurl的工作方式或程序运行中改变其工作方式,实现回调函数以完成用户特定任务。

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


curl_easy_setopt(curl, CURLOPT_URL, strUrl);
//设置接收数据的回调
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, DownloadCallback);
// 设置重定向的最大次数
curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 5);
// 遵循3xx重定向
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
//开启进度
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0);
//设置进度回调函数
#if LIBCURL_VERSION_NUM >= 0x072000
    curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, xferinfo);
    /* pass the struct pointer into the xferinfo function, note that this is
       an alias to CURLOPT_PROGRESSDATA */ 
    curl_easy_setopt(curl, CURLOPT_XFERINFODATA, &prog);
#else
    curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, older_progress);
    /* pass the struct pointer into the progress function */ 
    curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, &prog);
#endif

参数CURLoption

CURLOPT_VERBOSE

详细模式

CURLcode ret = curl_easy_setopt(handle, CURLOPT_VERBOSE, 1L);
启用 libcurl的详细模式后,它会在传输过程中向 stderr输出与传输相关的详细信息。这样就可以知道为什么传输会失败,并且可以了解向 libcurl 提出不同要求时,它会怎样做。可以通过
CURLOPT_STDERR 将输出重定向到其他位置,或者用调试回调获得更多信息(后文将提供进一步
说明)。

libcurl提供了一个跟踪回调,除了展示详细
模式所提供的内容外,它还会提供所有已发送和接收的数据,这样应用就可以获得完整的跟踪信
息了。
已发送和接收的数据以未加密的形式提供给跟踪回调,在使用 TLS 或基于 SSH 的协议时,
这会非常方便,因为这种情况下无法从网络捕获数据来进行调试。
设置 CURLOPT_DEBUGFUNCTION 选项后,仍然需要启用 CURLOPT_VERBOSE ,但如果设置了
跟踪回调,libcurl将用这个回调代替内部处理机制。
跟踪回调需要与以下原型匹配:


int my_trace(CURL *handle, curl_infotype type, char *ptr, size_t size,
void *userp);

其中 handle是句柄,type描述了传给回调的数据的类型(输入/输出数据、输入/输出标头、输入/
输出 TLS数据和“文本”),ptr指向传入数据,size是数据字节数,userp是使用 CURLOPT_DEBUGDATA
设置的自定义指针。
ptr指向的数据不会以 0结尾,而且与 size参数指定的大小完全相同。
回调必须返回 0,否则 libcurl会将其视为错误并中止传输。
curl官方网站上提供了一个叫作 debug.c的示例,其中包含了一个简单的跟踪功能以供参考。
CURLOPT_DEBUGFUNCTION 手册页中还有其他更多详细信息。

CURLOPT_URL

设置访问URL

#### CURLOPT_WRITEFUNCTION CURLOPT_WRITEDATA ####

写回调

回调函数原型为: size_t function( void *ptr, size_t size, size_t nmemb, void *stream); 函数将在 libcurl接收到数据后被调用,因此函数多做数据保存的功能,如处理下载文件。 CURLOPT_WRITEDATA 用于表明 CURLOPT_WRITEFUNCTION函数中的 stream指针的来源。

ptr指向收到的数据,使用 nmemb
乘以 size可以计算出数据的大小。

写回调可以接收尽可能多的数据,但不会做出任何假设。可能是一字节,也可能是数千字节。
传给写回调的最大数据是在curl.h头文件中定义的: CURL_MAX_WRITE_SIZE (通常默认值为 16 KB)。
如果启用了 CURLOPT_HEADER ,那么头部数据也将传给写回调,最多可以接受 CURL_MAX_HTTP_HEADER
字节的头部数据。通常是 100 KB

如果你没有通过 CURLOPT_WRITEFUNCTION属性给 easy handle设置回调函数, libcurl会提供一个默认的回调函数,它只是简单的将接收到的数据打印到标准输出。你也可以通过 CURLOPT_WRITEDATA属性给默认回调函数传递一个已经打开的文件指针,用于将数据输出到文件里。

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指针的来源。

CURLOPT_DEBUGFUNCTION

调试回调

debug_callback 函数必须与下面这个原型匹配:


int debug_callback(CURL *handle,
curl_infotype type,
char *data,
size_t size,
void *userdata);

这个回调函数替换了默认的详细输出函数,可用于查看所有的调试并跟踪消息,以帮助应用
了解正在发生的事情。 type 参数指定了数据类型:标头、数据或 SSL数据以及它的流向方向。
这个回调的常见用途是跟踪 libcurl发送和接收的所有数据。传给这个回调的数据是未经加密
的,即使使用了 HTTPS或其他加密协议。
这个回调必须返回 0,或者返回导致传输停止的错误码。
通过 userdata 参数传给回调的用户指针被设为 CURLOPT_DEBUGDATA :


curl_easy_setopt(handle, CURLOPT_DEBUGDATA, custom_pointer);
CURLOPT_SOCKOPTFUNCTION

sockopt 回调

sockopt_callback 函数必须与下面这个原型匹配:


int sockopt_callback(void *clientp,
curl_socket_t curlfd,
curlsocktype purpose);

创建好新套接字(进行连接前)后,libcurl会调用这个回调函数,以便应用可以修改特定的
套接字选项。

clientp指针指向用 CURLOPT_SOCKOPTDATA 设置的私有数据集:


curl_easy_setopt(handle, CURLOPT_SOCKOPTDATA, custom_pointer);

这个回调应该返回:

CURL_SOCKOPT_OK ,表示成功;CURL_SOCKOPT_ERROR ,让 libcurl知道出现了不可恢复的错误;CURL_SOCKOPT_ALREADY_CONNECTED ,表示成功,并且套接字已经连接到目标主机。
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指针来源。

当 libcurl想将数据发送给服务器时,就会调用这个回调函数。你可以通过这个传输上传数据
或以其他方式将数据发送给服务器。这个回调将被重复调用,直到所有数据都已传输或传输失败。

CURLOPT_NOPROGRESS CURLOPT_PROGRESSFUNCTION CURLOPT_PROGRESSDATA, CURLOPT_XFERINFOFUNCTION

进度回调

跟数据传输进度相关的参数。 CURLOPT_PROGRESSFUNCTION 指定的函数正常情况下每秒被 libcurl调用一次,为了使 CURLOPT_PROGRESSFUNCTION被调用, CURLOPT_NOPROGRESS必须被设置为false, CURLOPT_PROGRESSDATA指定的参数将作为CURLOPT_PROGRESSFUNCTION指定函数的第一个参数

可以用 CURLOPT_PROGRESSFUNCTION 设置旧
的回调,但新的回调是用 CURLOPT_XFERINFOFUNCTION 来设置的:


curl_easy_setopt(handle, CURLOPT_XFERINFOFUNCTION, xfer_callback);

xfer_callback 函数必须与下面这个原型匹配:


int xfer_callback(void *clientp, curl_off_t dltotal, curl_off_t dlnow,
curl_off_t ultotal, curl_off_t ulnow);

如果设置了这个选项,并且 CURLOPT_NOPROGRESS 被设为 0,libcurl就会以一定的时间间隔
调用这个回调函数。在传输数据时,它将被频繁调用,但速度较慢时,比如没有数据可传输时,调用频率将降到约每秒一次。

clientp指针指向被设置为 CURLOPT_XFERINFODATA 的私有数据:
curl_easy_setopt(handle, CURLOPT_XFERINFODATA, custom_pointer);
回调被告知 libcurl将传输和已传输的数据量,以字节数来表示。

dltotal 是指 libcurl期望下载的总字节数。dlnow 是指到目前为止已下载的字节数。ultotal 是指 libcurl期望上传的总字节数。ulnow 是指到目前为止已上传的字节数。
CURLOPT_TIMEOUT CURLOPT_CONNECTIONTIMEOUT

CURLOPT_TIMEOUT 由于设置传输时间, CURLOPT_CONNECTIONTIMEOUT 设置连接等待时间

CURLOPT_FOLLOWLOCATION

设置重定位URL

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,指定你希望开始传递的 偏移量。

CURLOPT_QUOTE,CURLOPT_POSTQUOTE

这两个选项的功能类似,它们的共同点都是给 FTP 或 SFTP 传递命令。这些命令应该放在 struct slist 链表中存储,使用时需要用 curl_slist_append() 函数将这些命令打包起来,然后一起发送出去。
它们的不同点是:CURLOPT_QUOTE 选项要求命令要在 FTP 传输请求之前就要发送到库,而 CURLOPT_POSTQUOTE 则可以在 FTP 传输请求发送完后发送。

参数parameter

parameter 这个参数 既可以是个函数的指针,也可以是某个对象的指针,也可以是个long型的变量.它用什么这取决于第二个参数.

curl_easy_perform() 实现传输任务


CURLcode curl_easy_perform(CURL * easy_handle );

返回0意味一切ok,非0代表错误发生。主要错误码说明:


CURLE_OK	任务完成一切都好
CURLE_UNSUPPORTED_PROTOCOL	不支持的协议,由URL的头部指定
CURLE_COULDNT_CONNECT	不能连接到remote 主机或者代理
CURLE_REMOTE_ACCESS_DENIED	访问被拒绝
CURLE_HTTP_RETURNED_ERROR	Http返回错误
CURLE_READ_ERROR	读本地文件错误

要获取详细的错误描述字符串,可以通过 const char *curl_easy_strerror(CURLcode errornum ) 这个函数取得.

https://curl.haxx.se/libcurl/c/libcurl-errors.html

curl_multi_perform

当设置好easy模式并准备传输的时候,可以使用curl_multi_add_handle替代curl_easy_perform,这样easy handler则会加入multi栈中。我们能在任何时候增加一个esay handler给multi模式,即使easy已经在执行传输操作了。

也可以在任何时候使用curl_multi_remove_handle将esay handler从multi栈中移出,一旦移出可以再次使用curl_easy_perform来进行传输任务。

添加easy handler到multi并不马上开始执行,由curl_multi_perform启动执行。
启动后将执行所有multi stack中的收发事件。如果栈上是空的直接返回。函数参数running_handles会返回当前还未结束的easy handler个数。

新的API中,官网更推荐使用curl_multi_wait的方式来实现,实现方便,同时也可以避免select中file descriptors不能大于1024的问题。调用curl_multi_info_read获取每一个执行完成的操作信息。在完成后即将其移除multi stack。

curl_easy_cleanup() 释放内存


curl_easy_cleanup() 

char *curl_version()


char *curl_version();

打印当前libcurl库的版本。

curl_slist_append与curl_slist_free_all

设置消息头


CURL *handle;
struct curl_slist *slist=NULL;
 
slist = curl_slist_append(slist, "pragma:");
 
if (slist == NULL)
  return -1;
 
curl_easy_setopt(handle, CURLOPT_HTTPHEADER, slist);
 
curl_easy_perform(handle);
 
curl_slist_free_all(slist); /* free the list again */

curl_easy_getinfo

https://curl.se/libcurl/c/curl_easy_getinfo.html

获取的是应答头中特定的信息,这个函数还能获取curl的一些内部信息,如请求时间、连接时间等等。


CURLcode curl_easy_getinfo(CURL *curl, CURLINFO info, ... );

例如


CURLcode res;
char *content_type;
res = curl_easy_getinfo(curl, CURLINFO_CONTENT_TYPE, &content_type);

CURLcode

查看错误返回值

libcurl error codes


CURLcode retcCode = curl_easy_perform(curl);
//查看是否有出错信息
const char* pError = curl_easy_strerror(retcCode);

要想在出现错误的情况下获得可读性稍强一些的错误消息,可以用 CURLOPT_ERRORBUFFER
选项指定程序中的一个缓冲区,libcurl会在返回错误之前将相关的错误消息放在这个缓冲区中:


curl error[CURL_ERROR_SIZE]; /* needs to be at least this big */
CURLcode ret = curl_easy_setopt(handle, CURLOPT_ERRORBUFFER, error);

例子

url2file.c


#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
 
#include <curl/curl.h>
 
static size_t write_data(void *ptr, size_t size, size_t nmemb, void *stream)
{
  size_t written = fwrite(ptr, size, nmemb, (FILE *)stream);
  return written;
}
 
int main(int argc, char *argv[])
{
  CURL *curl_handle;
  static const char *pagefilename = "page.out";
  FILE *pagefile;
 
  if(argc < 2) {
    printf("Usage: %s <URL>
", argv[0]);
    return 1;
  }
 
  curl_global_init(CURL_GLOBAL_ALL);
 
  /* init the curl session */ 
  curl_handle = curl_easy_init();
 
  /* set URL to get here */ 
  curl_easy_setopt(curl_handle, CURLOPT_URL, argv[1]);
 
  /* Switch on full protocol/debug output while testing */ 
  curl_easy_setopt(curl_handle, CURLOPT_VERBOSE, 1L);
 
  /* disable progress meter, set to 0L to enable and disable debug output */ 
  curl_easy_setopt(curl_handle, CURLOPT_NOPROGRESS, 1L);
 
  /* send all data to this function  */ 
  curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, write_data);
 
  /* open the file */ 
  pagefile = fopen(pagefilename, "wb");
  if(pagefile) {
 
    /* write the page body to this file handle */ 
    curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, pagefile);
 
    /* get it! */ 
    curl_easy_perform(curl_handle);
 
    /* close the header file */ 
    fclose(pagefile);
  }
 
  /* cleanup curl stuff */ 
  curl_easy_cleanup(curl_handle);
 
  curl_global_cleanup();
 
  return 0;
}

ftpget.c


#include <stdio.h>
 
#include <curl/curl.h>
 
/* <DESC>
 * Get a single file from an FTP server.
 * </DESC>
 */ 
 
struct FtpFile {
  const char *filename;
  FILE *stream;
};
 
static size_t my_fwrite(void *buffer, size_t size, size_t nmemb, void *stream)
{
  struct FtpFile *out = (struct FtpFile *)stream;
  if(!out->stream) {
    /* open file for writing */ 
    out->stream = fopen(out->filename, "wb");
    if(!out->stream)
      return -1; /* failure, can't open file to write */ 
  }
  return fwrite(buffer, size, nmemb, out->stream);
}


int main(void)
	{
	  CURL *curl;
	  CURLcode res;
	  struct FtpFile ftpfile = {
	    "curl.tar.gz", /* name to store the file as if successful */ 
	    NULL
	  };


  curl_global_init(CURL_GLOBAL_DEFAULT);
 
  curl = curl_easy_init();
  if(curl) {
    /*
     * You better replace the URL with one that works!
     */ 
    curl_easy_setopt(curl, CURLOPT_URL,
                     "ftp://ftp.example.com/curl/curl-7.9.2.tar.gz");
    /* Define our callback to get called when there's data to be written */ 
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, my_fwrite);
    /* Set a pointer to our struct to pass to the callback */ 
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, &ftpfile);
 
    /* Switch on full protocol/debug output */ 
    curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
 
    res = curl_easy_perform(curl);
 
    /* always cleanup */ 
    curl_easy_cleanup(curl);
 
    if(CURLE_OK != res) {
      /* we failed */ 
      fprintf(stderr, "curl told us %d
", res);
    }
  }
 
  if(ftpfile.stream)
    fclose(ftpfile.stream); /* close the local file */ 
 
  curl_global_cleanup();
 
  return 0;
}

progressfunc.c


#include <stdio.h>
#include <curl/curl.h>
 
#if LIBCURL_VERSION_NUM >= 0x073d00
/* In libcurl 7.61.0, support was added for extracting the time in plain
   microseconds. Older libcurl versions are stuck in using 'double' for this
   information so we complicate this example a bit by supporting either
   approach. */ 
#define TIME_IN_US 1  
#define TIMETYPE curl_off_t
#define TIMEOPT CURLINFO_TOTAL_TIME_T
#define MINIMAL_PROGRESS_FUNCTIONALITY_INTERVAL     3000000
#else
#define TIMETYPE double
#define TIMEOPT CURLINFO_TOTAL_TIME
#define MINIMAL_PROGRESS_FUNCTIONALITY_INTERVAL     3
#endif
 
#define STOP_DOWNLOAD_AFTER_THIS_MANY_BYTES         6000
 
struct myprogress {
  TIMETYPE lastruntime; /* type depends on version, see above */ 
  CURL *curl;
};
 
/* this is how the CURLOPT_XFERINFOFUNCTION callback works */ 
static int xferinfo(void *p,
                    curl_off_t dltotal, curl_off_t dlnow,
                    curl_off_t ultotal, curl_off_t ulnow)
{
  struct myprogress *myp = (struct myprogress *)p;
  CURL *curl = myp->curl;
  TIMETYPE curtime = 0;
 
  curl_easy_getinfo(curl, TIMEOPT, &curtime);
 
  /* under certain circumstances it may be desirable for certain functionality
     to only run every N seconds, in order to do this the transaction time can
     be used */ 
  if((curtime - myp->lastruntime) >= MINIMAL_PROGRESS_FUNCTIONALITY_INTERVAL) {
    myp->lastruntime = curtime;
#ifdef TIME_IN_US
    fprintf(stderr, "TOTAL TIME: %" CURL_FORMAT_CURL_OFF_T ".%06ld
",
            (curtime / 1000000), (long)(curtime % 1000000));
#else
    fprintf(stderr, "TOTAL TIME: %f 
", curtime);
#endif
  }
 
  fprintf(stderr, "UP: %" CURL_FORMAT_CURL_OFF_T " of %" CURL_FORMAT_CURL_OFF_T
          "  DOWN: %" CURL_FORMAT_CURL_OFF_T " of %" CURL_FORMAT_CURL_OFF_T
          "
",
          ulnow, ultotal, dlnow, dltotal);
 
  if(dlnow > STOP_DOWNLOAD_AFTER_THIS_MANY_BYTES)
    return 1;
  return 0;
}
 
#if LIBCURL_VERSION_NUM < 0x072000
/* for libcurl older than 7.32.0 (CURLOPT_PROGRESSFUNCTION) */ 
static int older_progress(void *p,
                          double dltotal, double dlnow,
                          double ultotal, double ulnow)
{
  return xferinfo(p,
                  (curl_off_t)dltotal,
                  (curl_off_t)dlnow,
                  (curl_off_t)ultotal,
                  (curl_off_t)ulnow);
}
#endif
 
int main(void)
{
  CURL *curl;
  CURLcode res = CURLE_OK;
  struct myprogress prog;
 
  curl = curl_easy_init();
  if(curl) {
    prog.lastruntime = 0;
    prog.curl = curl;
 
    curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/");
 
#if LIBCURL_VERSION_NUM >= 0x072000
    /* xferinfo was introduced in 7.32.0, no earlier libcurl versions will
       compile as they won't have the symbols around.
 
       If built with a newer libcurl, but running with an older libcurl:
       curl_easy_setopt() will fail in run-time trying to set the new
       callback, making the older callback get used.
 
       New libcurls will prefer the new callback and instead use that one even
       if both callbacks are set. */ 
 
    curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, xferinfo);
    /* pass the struct pointer into the xferinfo function, note that this is
       an alias to CURLOPT_PROGRESSDATA */ 
    curl_easy_setopt(curl, CURLOPT_XFERINFODATA, &prog);
#else
    curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, older_progress);
    /* pass the struct pointer into the progress function */ 
    curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, &prog);
#endif
 
    curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L);
    res = curl_easy_perform(curl);
 
    if(res != CURLE_OK)
      fprintf(stderr, "%s
", curl_easy_strerror(res));
 
    /* always cleanup */ 
    curl_easy_cleanup(curl);
  }
  return (int)res;
}

Http下载文件


static size_t write_data(void* ptr, size_t size, size_t nmemb, void* stream)
{
	size_t written = fwrite(ptr, size, nmemb, (FILE*)stream);
	return written;
}

void HttpDownload(std::string& strUrl, std::string& strFileName)
{
	CURL* curl_handle;
	FILE* pagefile;

	/* init the curl session */
	curl_handle = curl_easy_init();

	/* set URL to get here */
	curl_easy_setopt(curl_handle, CURLOPT_URL, strUrl.c_str());

	/* Switch on full protocol/debug output while testing */
	curl_easy_setopt(curl_handle, CURLOPT_VERBOSE, 1L);

	/* disable progress meter, set to 0L to enable and disable debug output */
	curl_easy_setopt(curl_handle, CURLOPT_NOPROGRESS, 1L);

	/* send all data to this function  */
	curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, write_data);

	/* open the file */
	errno_t err = fopen_s(&pagefile, strFileName.c_str(), "wb");
	if (err == err) {

		/* write the page body to this file handle */
		curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, pagefile);

		/* get it! */
		CURLcode res = curl_easy_perform(curl_handle);
		if (res != CURLE_OK)
		{
			const char* pError = curl_easy_strerror(res);
		}
		/* close the header file */
		fclose(pagefile);
	}

	/* cleanup curl stuff */
	curl_easy_cleanup(curl_handle);
}

中文乱码

获取的网页内容如果是UTF_8编码,需要转换为GBK编码


std::string UTF8ToGBK(const std::string& strUTF8)    
{    
	int len = MultiByteToWideChar(CP_UTF8, 0, strUTF8.c_str(), -1, NULL, 0);    
	WCHAR* wszGBK = new WCHAR[len+1];  
	memset(wszGBK, 0, len * 2 + 2);    
	MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)(LPCTSTR)strUTF8.c_str(), -1, wszGBK, len);    

	len = WideCharToMultiByte(CP_ACP, 0, wszGBK, -1, NULL, 0, NULL, NULL);    
	char *szGBK = new char[len + 1];    
	memset(szGBK, 0, len + 1);    
	WideCharToMultiByte(CP_ACP,0, wszGBK, -1, szGBK, len, NULL, NULL);     
	std::string strTemp(szGBK);    
	delete[]szGBK;    
	delete[]wszGBK;    
	return strTemp;    
} 

std::string GBKToUTF8(const std::string& strGBK)  
{  
	std::string strOutUTF8 = "";  
	WCHAR * str1;  
	int n = MultiByteToWideChar(CP_ACP, 0, strGBK.c_str(), -1, NULL, 0);  
	str1 = new WCHAR[n];  
	MultiByteToWideChar(CP_ACP, 0, strGBK.c_str(), -1, str1, n);  
	n = WideCharToMultiByte(CP_UTF8, 0, str1, -1, NULL, 0, NULL, NULL);  
	char * str2 = new char[n];  
	WideCharToMultiByte(CP_UTF8, 0, str1, -1, str2, n, NULL, NULL);  
	strOutUTF8 = str2;  
	delete[]str1;  
	str1 = NULL;  
	delete[]str2;  
	str2 = NULL;  
	return strOutUTF8;  
}

使用LibIconv

所需文件


iconv.exe
libcharset1.dll
libiconv2.dll
libintl3.dll

命令行


curl https://www.baidu.com | iconv.exe -f utf-8 -t gbk

代码:


int covert(char *desc, char *src, char *input, size_t ilen, char *output, size_t olen)
{
	char **pin = &input;
	char **pout = &output;
	iconv_t cd = iconv_open(desc, src);
	if (cd == (iconv_t)-1)
	{
		return -1;
	}
	memset(output, 0, olen);
	if (iconv(cd, (const char **)pin, &ilen, pout, &olen)) return -1;
	iconv_close(cd);
	return 0;
}

char *input = "中国";
size_t len = strlen(input);
char *output = (char *)malloc(OUTLEN);
covert("UTF-8", "GBK", input, len, output, OUTLEN);
printf("%s
", output);

BOOK

cURL必知必会

libcurl提供了三种方式来执行传输

(1)“easy”接口允许以同步的方式进行单个传输。libcurl执行传输,并在完成(不管是成功
还是失败)后将控制权返回给你的应用。

(2)“multi”接口允许同时进行多个传输,或者进行一个无阻塞的传输。
multi”的意思是多个,指多个并行传输,它们都在同一个单线程中完成。multi API是非阻
塞的,因此将它用于单次传输也是可以的。
这个时候仍然可以像之前那样在“easy” CURL * 句柄中设置传输信息,但使用 multi接口时,
需要创建一个 CURLM * 句柄,并用它来驱动所有的传输。multi句柄可以“容纳”一个或多个 easy
句柄:


CURLM *multi_handle = curl_multi_init();

将 easy句柄添加到 multi句柄非常简单:


curl_multi_add_handle( multi_handle, easy_handle );

(3)“multi_socket”接口是常规 multi接口的一个小变体,它是基于事件的,如果打算同时进
行数百甚至数千个传输,那么最好使用这个 API。

现在有很多基于事件的系统可供选择,而 libcurl与你所使用的系统完全无关。libevent、libev
和 libuv 是三个比较流行的库,不过你也可以直接使用操作系统的原生解决方案,如 epoll、
kqueue、/dev/poll、pollset、Event Completion或 I/O Completion Ports。

curl --libcurl

用户先尝试用 curl命令行工具执行传输,在它基本可以按照预期方式运行之后,再
将 --libcurl [filename] 选项附加到命令行之后,然后再运行一次。
–libcurl 命令行选项会在指定的文件中创建一个 C 程序。如果你将文件名指定为单个破折号,如 --libcurl - ,那么 C程序会被写到 stdout而不是文
件中。

用 libcurl 执行 HTTP 操作

响应正文被传给写入回调,响应标头被传给标头回调,但有时应用只想知道数据的大小。
可以用 curl_easy_getinfo() 提取服务器在标头中指明的响应大小,如下所示:


double size;
curl_easy_getinfo(curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &size);

还可以等到传输完成后再获取大小,这是一种更可靠的方式,因为并非所有的 URL 都会预
先提供响应大小(比如有些服务器是按需生成内容的),你可以获取最近的传输下载了多少数据。


double size;
curl_easy_getinfo(curl, CURLINFO_SIZE_DOWNLOAD, &size);

可以在传输完成后提取响应码:


long code;
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code);

如果希望 curl将 400及以上的 HTTP响应码视为错误,libcurl为此提供了 CURLOPT_FAILONERROR
选项,以便 curl在这种情况下返回 CURLE_HTTP_RETURNED_ERROR 。这样它就会尽快返回错误,
而不会返回响应正文。

通常,用 libcurl执行传输时,你所使用的选项会隐含特定的请求方法。如果只是简单地指定
一个 URL,那么它会默认使用 GET ,而如果设置了 CURLOPT_POSTFIELDS ,那么 libcurl将会使用
POST 。如果将 CURLOPT_UPLOAD 设置为 true,libcurl会在 HTTP请求中使用 PUT ,其他的类似。

如果设置了 CURLOPT_NOBODY ,那么 libcurl会使用 HEAD 。
不过,有时这些默认的 HTTP方法不是你希望使用的方法。可以通过 CURLOPT_CUSTOMREQUEST
让 libcurl使用你指定的方法。例如,你可以像下面这样向指定的 URL发送 DELETE 方法:


curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE");
curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/file.txt");

修改标头
如果其中一个默认标头的值无法满足你的需求,那么可以修改它们。假设你认为默认 Host:
标头的值不对(即使它是从你指定的 URL派生出来的),那么可以设置自己的值:


struct curl_slist *list = NULL;
list = curl_slist_append(list, "Host: Alternative");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list);
curl_easy_perform(curl);
curl_slist_free_all(list); /* 再次释放列表 */

可以通过 CURLOPT_RANGE 选项让 libcurl请求一定区间内的数据。假设想要前 200个字节:


curl_easy_setopt(curl, CURLOPT_RANGE, "0-199");

用户名和密码
如果没有指定用户名,libcurl将不会进行 HTTP身份验证。可以按照以下方式设置用户名:


curl_easy_setopt(curl, CURLOPT_USERNAME, "joe");

当然,大多数身份验证还需要单独设置密码:


curl_easy_setopt(curl, CURLOPT_PASSWORD, "secret");

这样就可以让 libcurl为这次传输启用默认的身份验证方法:HTTP Basic。

客户端本身并不知道是否需要发送身份验证请求,这应该是服务器提出的要求。当受保护的
服务器资源需要身份验证时,它将返回 401 HTTP响应码和 WWW-Authenticate: 标头。标头中包
含了被请求资源可以接受哪些身份验证方法的信息。

Basic 是默认的 HTTP 身份验证方法,顾名思义,它确实很基础。它接受以冒号分隔的用名和密码,并在将它们放入 Authorization: 标头前对其进行 base64编码。
如果用户名和密码像上面那样设置,那么发送出去的标头如下所示:


Authorization: Basic am9lOnNlY3JldA==

这种身份验证方法非常不安全,因为凭证将以明文形式在网络上发送。
你可以明确要求 libcurl使用 Basic方法进行特定的传输,如下所示:


curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);

cookie 引擎

启用 cookie引擎的方法有两种。

通过读取启用 cookie 引擎

通过 CURLOPT_COOKIEFILE 选项让 libcurl将给定文件中的 cookie读入 easy句柄:


curl_easy_setopt(easy, CURLOPT_COOKIEFILE, "cookies.txt");

常见的技巧是只指定一个不存在的文件名或空字符“”,这样就可以启动一个带有空白 cookie
存储的 cookie引擎。
这个选项可以设置多次,每个给定的文件都会被读取。

通过写入启用 cookie 引擎

通过 CURLOPT_COOKIEJAR 选项将收到的 cookie存储在文件中:


curl_easy_setopt(easy, CURLOPT_COOKIEJAR, "cookies.txt"

稍后用 curl_easy_cleanup() 关闭 easy句柄时,所有已知的 cookie都将被写入指定的文件。
文件格式是众所周知的“Netscape cookie文件”格式,浏览器也曾经使用过这种格式。

不向 cookie存储中添加 cookie,甚至不激活 cookie引擎,同时又要在请求中发送一组特定的
cookie,最简单且最直接的方法是用 CURLOPT_COOKIE 设置 cookie:


curl_easy_setopt(easy, CURLOPT_COOKIE, "name=daniel; present=yes;");

导入和导出

将 cookie 添加到 cookie 存储

要想将新 cookie添加到 cookie存储中,只需要通过 CURLOPT_COOKIELIST 选项将要添加的
新 cookie 传给 curl。输入格式为 cookie 文件格式中的单行,或者格式化为 Set-Cookie: 响应标
头,我们建议使用 cookie文件单行:


#define SEP "	" /* Tab 分隔的字段 */
char *my_cookie =
"example.com" /* 主机名 */
SEP "FALSE" /* 包含子域名 */
SEP "/" /* 路径 */
SEP "FALSE" /* 是否安全 */
SEP "0" /* 过期时间,0 表示会话
cookie */
SEP "foo" /* 名字 */
SEP "bar"; /* 值 */
curl_easy_setopt(curl, CURLOPT_COOKIELIST, my_cookie);

如果给定的 cookie 与现有 cookie(具有相同的域名和路径等)匹配,那么就用新内容覆盖
旧 cookie。

从 cookie 存储获取所有 cookie
有时候,在关闭句柄时写入 cookie文件还不够,应用可以选择从存储中获取所有当前已知的cookie,如下所示:

struct curl_slist *cookies
curl_easy_getinfo(easy, CURLINFO_COOKIELIST, &cookies);

这将返回一个指向 cookie 链表的指针,每个 cookie 变为 cookie 文件格式的单行。列表已经
为你分配好了,因此,应用使用完这些信息后,不要忘记调用 curl_slist_free_all 。

cookie 存储命令
如果设置和获取 cookie还不够,那么还可以通过更多方式操作 cookie存储。
可以用以下方法移除整个内存 cookie存储:

curl_easy_setopt(curl, CURLOPT_COOKIELIST, “ALL”);

从内存中清除所有会话 cookie(没有过期日期的 cookie):


curl_easy_setopt(curl, CURLOPT_COOKIELIST, "SESS");

强制将所有 cookie写入之前通过 CURLOPT_COOKIEJAR 选项指定的文件中:


curl_easy_setopt(curl, CURLOPT_COOKIELIST, "FLUSH");

强制从之前通过 CURLOPT_COOKIEFILE 选项指定的文件中重新加载 cookie:


curl_easy_setopt(curl, CURLOPT_COOKIELIST, "RELOAD");

下载

由于 easy 句柄中设置的选项在发生更改之前一直保持不变,但有时候你可能需要使用 GET
之外的方法,然后在后续请求中又想切换回 GET ,对于这种情况,则可以使用 CURLOPT_HTTPGET
选项:


curl_easy_setopt(easy, CURLOPT_HTTPGET, 1L);

同时下载标头

可以通过 CURLOPT_HEADER 选项让 libcurl将标头传给与常规正文相同的“流”:


easy = curl_easy_init();
curl_easy_setopt(easy, CURLOPT_HEADER, 1L);
curl_easy_setopt(easy, CURLOPT_URL, "http://example.com/");
curl_easy_perform(easy);

还可以选择将标头存储在单独的下载文件中,这个需要依赖写入回调和标头回调的默认
行为:


easy = curl_easy_init();
FILE *file = fopen("headers", "wb");
curl_easy_setopt(easy, CURLOPT_HEADERDATA, file);
curl_easy_setopt(easy, CURLOPT_URL, "http://example.com/");
curl_easy_perform(easy);
fclose(file);

如果只是想随便浏览一下标头,那么可以在开发时设置详细模式,这样就可以在 stderr中看
到传出和传入的标头:

curl_easy_setopt(easy, CURLOPT_VERBOSE, 1L);

上传

HTTP POST

POST 是将数据传到远程 Web 应用最常用的 HTTP 方法。在浏览器中最常见的方法是填写
HTML表单,然后按下提交按钮。它是 HTTP请求将数据传到服务器的标准方式。在使用 libcurl
时,通常需要指定指向数据的指针和数据的大小:


curl_easy_setopt(easy, CURLOPT_POSTFIELDS, dataptr);
curl_easy_setopt(easy, CURLOPT_POSTFIELDSIZE, (long)datalength);

或者你告诉 libcurl,这是一个 POST 请求,然后通过读取回调让 libcurl获取数据:


curl_easy_setopt(easy, CURLOPT_POST, 1L);
curl_easy_setopt(easy, CURLOPT_READFUNCTION, read_callback);

这种“常规的” POST 还会自动设置请求标头 Content-Type:application/x-www-form-
urlencoded 。

HTTP multipart formpost

multipart formpost仍然使用相同的 POST 方法,区别在于请求正文的格式。multipart formpost
基本上是一系列独立的“部分”,以 MIME风格的边界字符串分隔。你可以发送任意多个部分。
每个部分都有一个名字、一组标头和一些其他属性。
libcurl 提供了一个辅助函数来构建部分,并将它们发送给服务器。 curl_formadd 就是用来
构建 formpost的函数。调用这个函数一次就会创建一个部分,你需要将部分的细节和特征作为参
数传给这个函数。添加完要发送的所有部分后,将 curl_formadd 返回的句柄传给
curl_easy_setopt ,如下所示:


curl_easy_setopt(easy, CURLOPT_HTTPPOST, formposthandle);

语言绑定

C++ http://curlpp.org/ Jean-Philippe, Barrette-LaPierreCh/C++ https://chcurl.sourceforge.io/ Stephen Nestinger, Jonathan RogadoCocoa (BBHTTP) https://github.com/brunodecarvalho/BBHTTP Bruno de CarvalhoCocoa (CURLHandle) https://github.com/karelia/curlhandle/ Dan Woodglib/GTK+ http://atterer.org/glibcurl Richard AttererGo https://github.com/andelf/go-curl ShuYu WangJava https://github.com/pjlegato/curl-java Paul Legato.NET https://github.com/masroore/CurlSharp Masroor Ehsan Choudhury, Jeffrey PhillipsNodeJS https://github.com/JCMais/node-libcurl Jonathan Cardoso MachadoPascal/Delphi/Kylix https://curlpas.sourceforge.io/curlpas/ Jeffrey PohlmeyerPerl https://github.com/szbalint/WWW–Curl Cris Bailiff and Bálint SzilaksziPHP https://php.net/curl Sterling HughesPostgreSQL https://github.com/pramsey/pgsql-http Paul RamseyPython (PycURL) https://github.com/pycurl/pycurl Kjetil JacobsenR https://cran.r-project.org/package=curl Jeroen Ooms, Hadley Wickham, RStudioRuby (curb) https://github.com/taf2/curb Ross BamfordRuby (ruby-curl-multi) http://curl-multi.rubyforge.org/ Kristjan Petursson and Keith RarickRust (curl-rust) https://github.com/carllerche/curl-rust Carl LercheScilab https://help.scilab.org/docs/current/fr_FR/getURL.html Sylvestre LedruwxWidgets https://wxcode.sourceforge.io/components/wxcurl/ Casey O’Donnell

参考

libcurl库(C++)快速使用

C++ 用libcurl库进行http通讯网络编程

https://blog.csdn.net/tianzhaixing2013/article/details/74726040

http://my.huhoo.net/archives/2008/02/libiconv.html

21 个 curl 命令练习

  • 全部评论(0)
手机二维码手机访问领取大礼包
返回顶部