libcurl简介

1 简介

存在这样的应用场景,客户端需要从web服务器上面下载一些文件。要实现这个功能有以下几种选择:

1)利用SOCKET,自己实现HTTP中的Get请求。

2)利用现有的库实现。

虽然自己完成一个发送Get请求的模块并不困难,但是考虑到通过url获取文件的模块应该是软件当中的一项基础设施,一定存在可复用的模块。所以还是尽量用现成的东西去做,避免重复造轮子。

这时,找到了libcurl库。它有如下优点:开源、接口简单(C类型的接口)、轻巧。

下面内容的组织结构如下:第2节给一个实例关于如何利用libcurl库实现文件的下载。第3、4节是Libcurl接口的分类、结合一个例子介绍multi接口使用、注意事项。第5节对easy接口和multi接口做了总结和比较。

 

2 使用Libcurl下载文件的一个例子

/*下载url的文件*/
void UseLibcurl(const char* strUrl)
{
	CURL *curl_handle;
	const char *bodyfilename = "body.png";		//文件路径
	FILE *bodyfile;

	/* 全局初始化*/
	 curl_global_init(CURL_GLOBAL_ALL);
	/* easy 接口初始化*/
	curl_handle = curl_easy_init();

	/* 设置URL */
	curl_easy_setopt(curl_handle, CURLOPT_URL, strUrl);

	/* 设置写数据函数*/
	curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, write_data);

	/* 打开文件*/
	bodyfile = fopen(bodyfilename,"wb");		
	if (bodyfile == NULL) {
		curl_easy_cleanup(curl_handle);
		return;
	}
	/* 设置报文的主体写到文件当中而不是标准输出 */
	curl_easy_setopt(curl_handle,   CURLOPT_WRITEDATA, bodyfile);

	/* 执行请求 */
	res = curl_easy_perform(curl_handle);
	/* 关闭文件 */
	fclose(bodyfile);

	/* 清理easy句柄*/
	curl_easy_cleanup(curl_handle);
	/* 全局清理 */
	curl_global_cleanup();
}

通过简单的接口调用,可以实现文件的下载。同时Libcurl封装了文件的读写部分,可以写到标准输出或者交给用户的回调函数当中。

 

3 Libcurl接口的分类

Libcurl中接口分为4种类型:全局环境接口(globalinterface)、easy类型接口(easy interface)、multiple类型接口(multiple interface)、shared类型接口(shared interface)。

Globalinterface: libcurl的全局环境变量必须在程序初运行中建立和维持。初始化时必须调用curl_global_init,结束时必须调用curl_global_cleanup接口。值得注意的是,global接口不保证线程安全。必须在其他线程没有建立时调用。在C++中可以在一个静态变量的构造函数当中使用curl_global_init,析构函数当中使用curl_global_cleanup。

Easy interface:easy接口是同步的。当调用curl_easy_perform时,就会执行文件的传输。当网络传输完毕,函数结束。

Multiinterface:mutli接口则是异步接口。使用者可以在数据流传输的过程做一些处理,可以使用一个线程进行多个文件同时下载。The multi interface allows you to select()。

Shared interface:用于创建共享资源,被多个线程共用。通过使用curl_share_setopt设置共享的数据类型,目前支持的类型是DNS和COOKIE数据。共享数据由用户保证资源的互斥。必须用curl_share_setopt提供互斥的回调函数来保护资源。

 

4 一个关于Multi interface的例子

int main(int argc, char **argv)
{
  CURL *http_handle;
  CURL *http_handle2;
  CURLM *multi_handle;

  int running_hanlde; /* keep number of running handles */

  http_handle = curl_easy_init();
  http_handle2 = curl_easy_init();

  /* 任务1的url */
  curl_easy_setopt(http_handle, CURLOPT_URL, "http://www.haxx.se/");

  /* 任务2的url */
  curl_easy_setopt(http_handle2, CURLOPT_URL, "http://localhost/");

  /* 多任务句柄 */
  multi_handle = curl_multi_init();

  /* 添加任务 */
  curl_multi_add_handle(multi_handle, http_handle);
  curl_multi_add_handle(multi_handle, http_handle2);

  /* 开始执行请求 */
  while(CURLM_CALL_MULTI_PERFORM ==
        curl_multi_perform(multi_handle, &running_hanlde));

  while(running_hanlde) {
    struct timeval timeout;
    int rc; /* select() return code */
    fd_set fdread;
    fd_set fdwrite;
    fd_set fdexcep;
    int maxfd;
    FD_ZERO(&fdread);
    FD_ZERO(&fdwrite);
    FD_ZERO(&fdexcep);

    /* 设置超时时间和重连次数 */
    timeout.tv_sec = 1;
    timeout.tv_usec = 0;

    /* 获取文件描述信息*/
    curl_multi_fdset(multi_handle, &fdread, &fdwrite, &fdexcep, &maxfd);

    rc = select(maxfd+1, &fdread, &fdwrite, &fdexcep, &timeout);

    switch(rc) {
    case SOCKET_ERROR:
      /* select 错误*/
      break;
    case 0:
    default:
      /* 超时或socket可读写 */
      while(CURLM_CALL_MULTI_PERFORM ==
            curl_multi_perform(multi_handle, &running_hanlde));
      break;
    }
  }
  curl_multi_cleanup(multi_handle);

  curl_easy_cleanup(http_handle);
  curl_easy_cleanup(http_handle2);

  return 0;
}

这个例子使用multi接口执行两个url请求。

curl_multi_fdset返回内部socket的信息。接下来调用select,一旦拥有可读或可写的socket,就应该立即调用curl_multi_perform。只要返回值是CURLM_CALL_MULTI_PERFORM,需要一直调用curl_multi_perform接口。

 

总结

Easy接口的编程模型

Easy接口都是单线程模型,调用简单,适用于单个url请求的场景。

 

Multi接口的编程模型

Multi接口则是多线程模型。当有多个任务需要同时交替进行时,则需要使用这种方式。如果使用多个线程来分别执行easy请求,每个请求是单独完成的。可以根据实际需求选择使用方式。

 

      

      

      

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值