libcurl 是一个优秀的网络请求库,支持多种协议, 多平台, 这里我们就现简述一下http协议的使用
准备
本教程是在linux环境下编译和使用的
- 使用libcurl必须先编译,这个问题的话官方的仓库有简述, 我们就不再赘述,直接上仓库地址
- https://github.com/curl/curl
- 导入头文件 #include<curl/curl.h>
步骤
- 初始化curl
- 创建easy_handle
- 设置请求参数
- 对输出的数据进行回调的数据处理
- 创建一个容器存储数据
- 清理curl(全局 和 局部)
范例
#include <iostream>
#include <future>
#include <string>
#include <sstream>
#include <stdexcept>
#include "curl/curl.h"
size_t write_data(void* buffer, size_t size, size_t nmemb, void* userp)
{
//将void* 指针转换成char* 指针, 并且读取对应长度
std::string data((char*) buffer, size * nmemb);
//输出到开发者设置的数据类型中, 这里是stringstream
*((std::stringstream*) userp) << data << std::endl;
return size * nmemb;
}
std::string network(const std::string& url)
{
//《正题开始, 关注这个部分》
//创建一个easy_handle, 不要在线程直接共享easy_handle
CURL* easy_handle = curl_easy_init();
//数据输出存储的对象
std::stringstream out;
//检查是否创建成功
if(easy_handle == NULL)
{
//抛出错误异常
throw std::runtime_error("create easy_handle fail");
}
curl_easy_setopt(easy_handle, CURLOPT_URL, url.c_str());
//如果不提供这样的回调函数,那个curl只是简单的把数据输出到标准输出流中
curl_easy_setopt(easy_handle, CURLOPT_WRITEFUNCTION, &write_data);
//绑定数据输出
curl_easy_setopt(easy_handle, CURLOPT_WRITEDATA, &out);
//开始进行请求
curl_easy_perform(easy_handle);
//清理curl_easy
curl_easy_cleanup(easy_handle);
return out.str();
}
void printRet(std::future<std::string>& future)
{
//异常处理
try {
//获取请求的结果,为了避免住主线程堵塞, 我们在子线程中等待结果完成
std::string ret = future.get();
//输出结果
std::cout << "curl result:" << ret << std::endl;
} catch (std::exception& e)
{
e.what();
}
}
int main(int argc, char* args[])
{
//1.使用curl需要进行全局初始化,支持ssl
curl_global_init(CURL_GLOBAL_SSL);
//2.请求地址
std::string url = "https://www.baidu.com";
//3.这里我们使用异步来处理网络请求的任务
std::packaged_task<std::string(std::string)> task(network);
std::future<std::string> ret = task.get_future();
//4.将任务移新的线程中去, std::move, std::ref 分别对应右值移动, 和引用绑定
std::thread t = std::thread(std::move(task), std::ref(url));
//5.开辟另外一个线程处理数据
std::thread t2 = std::thread(std::move(printRet), std::ref(ret));
//TODO:此处做其他事情
//6.最后我们等待子线程处理任务完成
t.join();
t2.join();
//7.清理全局curl
curl_global_cleanup();
return 0;
}
同步请求
#include <iostream>
#include <thread>
#include <cstring>
#include <curl/multi.h>
#include "curl/multi.h"
static const char* urls[] = {
"https://www.baidu.com",
"https://www.baidu.com",
};
#define MAX 2
size_t write_data(char* buffer , size_t size, size_t nmemb, void* userp)
{
(void) buffer;
(void) userp;
return size * nmemb;
}
void init(CURLM* cm, int i)
{
CURL* easy_handle = curl_easy_init();
const char* url = urls[i];
curl_easy_setopt(easy_handle, CURLOPT_WRITEFUNCTION, write_data);
curl_easy_setopt(easy_handle, CURLOPT_URL, urls[i]);
//保存请求地址
curl_easy_setopt(easy_handle, CURLOPT_PRIVATE, urls[i]);
/添加到并发curlm 句柄中
curl_multi_add_handle(cm, easy_handle);
}
int main()
{
CURLM* cm;
CURLMsg* msg;
int max_fd, msg_left, still_running = -1;
long curl_timeo;
//fd_set结构体,本质上是是long类型的数组
fd_set fd_write, fd_read, fd_except;
struct timeval T;
curl_global_init(CURL_GLOBAL_ALL);
//初始化curlm
cm = curl_multi_init();
curl_multi_setopt(cm, CURLMOPT_MAXCONNECTS, (long)MAX);
for (int i = 0; i < MAX; ++i) {
init(cm, i);
}
do {
curl_multi_perform(cm, &still_running);
if(still_running)
{
FD_ZERO(&fd_read);
FD_ZERO(&fd_write);
FD_ZERO(&fd_except);
//获取需要监听的文件描述符,分别存放在fd_set 类型的数据中
if(curl_multi_fdset(cm, &fd_read, &fd_write, &fd_except, &max_fd) != CURLM_OK)
{
fprintf(stderr, "curl_multi_fdset");
return EXIT_FAILURE;
}
if(curl_multi_timeout(cm, &curl_timeo) != CURLM_OK)
{
fprintf(stderr, "curl_multi_timeout");
return EXIT_FAILURE;
}
if(curl_timeo == -1)
{
curl_timeo = 100;
}
//如果max_fd 等于就休眠一段时间后,继续执行curl_multi_perform
if(max_fd == -1)
{
std::this_thread::sleep_for(std::chrono::microseconds(100));
}
else
{
//计算秒
T.tv_sec = curl_timeo / 1000;
//计算毫秒
T.tv_usec = (curl_timeo % 1008) * 1000;
if (select(max_fd + 1, &fd_read, &fd_read, &fd_except, &T) < 0)
{
fprintf(stderr, "E: select(%i,,,,%li): %i: %s\n",
max_fd+1, curl_timeo, errno, std::strerror(errno));
return EXIT_FAILURE;
}
}
}
} while (still_running);
//IO读写完成,执行读取操作
while ((msg = curl_multi_info_read(cm, &msg_left)))
{
//检测数据是否完整
if(msg->msg == CURLMSG_DONE)
{
char* url;
//获取easy_handle 句柄
CURL* easy_handle = msg->easy_handle;
//使用句柄.获取请求的URL地址
curl_easy_getinfo(easy_handle, CURLINFO_PRIVATE, &url);
fprintf(stderr, "result:%d - %s <%s>", msg->data.result, curl_easy_strerror(msg->data.result), url);
curl_multi_remove_handle(cm, easy_handle);
curl_easy_cleanup(easy_handle);
}
else
{
fprintf(stderr, "CURLMsg: %s", msg->msg);
}
}
return 0;
}
网友评论