[转] libcurl异步方式使用总结(附流程图)
文为转载,原文地址:libcurl异步方式使用总结
实习期间用到了libcurl来做HTTPS双向认证,用的是异步方式,简单总结一下。
libcurl这个库的同步方式很简单,不做介绍,而异步方式很难理解,本博客参考官网的demo讲解,刚开始看可能很蒙,最后会整合全流程。
使用步骤如下:
1.初始化创建一个multi句柄:
CURLM *multi = curl_multi_init();
2.对multi句柄设置socket回调和timer回调:
curl_multi_setopt(multi, CURLMOPT_SOCKETFUNCTION, multi_sock_cb);
curl_multi_setopt(multi, CURLMOPT_SOCKETDATA, ¶m);
curl_multi_setopt(multi, CURLMOPT_TIMERFUNCTION, multi_timer_cb);
curl_multi_setopt(multi, CURLMOPT_TIMERDATA, ¶m);
3.对multi句柄添加easy句柄,异步开始:
CURL *easy = curl_easy_init();
curl_easy_setopt(conn->easy, CURLOPT_URL, url);
curl_easy_setopt(conn->easy, CURLOPT_WRITEFUNCTION, write_cb); // 负责读入数据的函数
curl_easy_setopt(conn->easy, CURLOPT_WRITEDATA, &data);
curl_multi_add_handle(multi, easy);
先看看第三行设置的write_cb
,该函数是你读入数据的函数:
/*
* ptr 指向libcurl库读到的数据
* data 用户自定义的缓冲区, 上面第四行设置
*/
size_t write_cb(void *ptr, size_t size, size_t nmemb, void *data) {
// 把ptr指向的数据拷到data
}
在curl_multi_add_handle
运行结束的那一刻,第2步设置的multi_timer_cb
马上被拉起执行,让我们看看multi_timer_cb
的函数声明:
/*
* multi 第一步创建的句柄
* timeout_ms libcurl库维护的一个超时时间,具体怎么算不清楚,回调时会自动赋值
* param 第二步设置的参数
* return 错误码
*/
int multi_timer_cb(CURLM *multi, long timeout_ms, void *param)
libcurl库本身没有定时器功能,只是告诉你一个定时时间timeout_ms
,这就要求我们自己维护一个定时器和到期的回调函数timer_cb
。
伪代码表示如下:
int multi_timer_cb(CURLM *multi, long timeout_ms, void *param) {
timer_.add(timer_cb, ms); // ms后执行timer_cb
}
timer_cb
主要调用libcurl的两个函数:
void timer_cb(param...) {
CURLMcode rc;
rc = curl_multi_socket_action(multi, CURL_SOCKET_TIMEOUT, ,
&still_running);
while((msg = curl_multi_info_read(multi, &msgs_left))) { // 判断数据是否读完
if(msg->msg == CURLMSG_DONE) {
// 清理资源操作
}
}
}
而multi_sock_cb
类似如此:
/*
* e 第三步添加的easy句柄
* s libcurl创建维护的socket
* what 执行动作(读或写)
*/
int multi_sock_cb(CURL *e, curl_socket_t s, int what, void *cbp, void *sockp)
在libcurl维护的socket描述符发生状态改变时(变回可读或可写),multi_sock_cb才会被回调。注意,函数回调时,第二个参数是socket描述符,这是libcurl维护创建的,但是你把它添加到poller(代指epoll或poll的封装类)或者libev等事件触发器中去,并设置回调函数,伪代码如下
int multi_sock_cb(CURL *e, curl_socket_t s, int what, void *cbp, void *sockp) {
poller.add(s, socket_cb); // 当描述符可读和可写时,调用socket_cb
}
看到这里是不是懵逼,不要急,最后会讲解全流程。socket_cb
里也是调用两个libcurl函数:
void socket_cb(param...) {
CURLMcode rc;
rc = curl_multi_socket_action(multi, CURL_SOCKET_TIMEOUT, ,
&still_running);
while((msg = curl_multi_info_read(multi, &msgs_left))) { // 判断数据是否读完
if(msg->msg == CURLMSG_DONE) {
// 清理资源操作
}
}
}
好了,函数写成这样就差不多了(都是伪代码,具体用法还是看demo)。那么这代码到底是怎么执行的呢,请看下图。
1、在curl_multi_add_handle之后,multi_timer_cb会马上被拉起调用,然后第一次调用的话timeout是0ms,所以timer_cb也会被拉起,然后调用curl_multi_socket_action。
2、此时,请注意在curl_multi_add_handle之前已经设置过了url了,所以此时是需要发起http请求,即写请求,所以在curl_multi_socket_action中libcurl会创建一个socket描述符,然后状态变为可写。
3、此时,因为libcurl的socket描述符状态发生改变,所以multi_sock_cb会被拉起,multi_sock_cb中就把socket描述符添加到poller中,设置写事件的回调函数为socket_cb。
4、因为socket描述符是可写的,所以poller会调用sock_cb,curl_multi_socket_action又被调用,而此函数就会发送http请求(即libcurl负责写fd)。
5、等到http请求被发送完,就需要接收响应,所以libcurl会把socket描述符从写状态改为读状态。
6、因为socket描述符变为可读,状态改变,multi_sock_cb又被调用,此时在poller中,将socket描述符的读事件回调函数设置为socket_cb。
7、当响应到来的时候,socket描述符可读,调用socket_cb,从而调用curl_multi_socket_action,该函数就就会异步调用之前设置的、负责读入数据的write_cb,从而读入数据。
8、 不断重复上一个步骤,直到数据被读完,此时libcurl会把socket描述符设置为删除状态,所以multi_sock_cb会被回调,负责清理资源。而且,curl_multi_info_read会判断已经读完数据,可以在这里进行数据转发,最终进行资源清理。注意,最终读到的数据,会在write_cb设置的data中(前提是你有在write_cb中保存下来哈哈哈~)。
总结:
这库使用起来十分奇怪,我看了几天才看懂用法,我这篇博文写得十分简陋,最好的学习方法还是把demo跑一遍,看看打印出来的日志,还有详细的参数设置,需要去看官网文档。
[转] libcurl异步方式使用总结(附流程图)的更多相关文章
- C# 的TCP Socket (异步方式)
简单的c# TCP通讯(TcpListener) C# 的TCP Socket (同步方式) C# 的TCP Socket (异步方式) C# 的tcp Socket设置自定义超时时间 C# TCP ...
- MVC Ajax Helper或jQuery异步方式加载部分视图
Model: namespace MvcApplication1.Models { public class Team { public string Preletter { get; set; } ...
- boost::asio 的同、异步方式
转自:http://blog.csdn.net/zhuky/archive/2010/03/10/5364574.aspx Boost.Asio是一个跨平台的网络及底层IO的C++编程库,它使用现代C ...
- Python并发编程-进程池及异步方式
进程池的基本概念 为什么有进程池的概念 效率问题 每次开启进程,都需要开启属于这个进程的内存空间 寄存器,堆栈 进程过多,操作系统的调度 进程池 python中的 先创建一个属于进程的池子 这个池子指 ...
- MVC4 Action 两种异步方式
1. xxxAsync/xxxCompleted 组合方式异步,xxxCompleted 就是他的回调函数,在执行完 xxxAsync 后调用xxxCompleted . 使用 异步方式必须继承A ...
- 反爬虫:利用ASP.NET MVC的Filter和缓存(入坑出坑) C#中缓存的使用 C#操作redis WPF 控件库——可拖动选项卡的TabControl 【Bootstrap系列】详解Bootstrap-table AutoFac event 和delegate的分别 常见的异步方式async 和 await C# Task用法 c#源码的执行过程
反爬虫:利用ASP.NET MVC的Filter和缓存(入坑出坑) 背景介绍: 为了平衡社区成员的贡献和索取,一起帮引入了帮帮币.当用户积分(帮帮点)达到一定数额之后,就会“掉落”一定数量的“帮帮 ...
- boost::ASIO的同步方式和异步方式
http://blog.csdn.net/zhuky/article/details/5364574 http://blog.csdn.net/zhuky/article/details/536468 ...
- ES transport client底层是netty实现,netty本质上是异步方式,但是netty自身可以使用sync或者await(future超时机制)来实现类似同步调用!因此,ES transport client可以同步调用也可以异步(不过底层的socket必然是异步实现)
ES transport client底层是netty实现,netty本质上是异步方式,但是netty自身可以使用sync或者await(future超时机制)来实现类似同步调用! 因此,ES tra ...
- 源码分析 Kafka 消息发送流程(文末附流程图)
温馨提示:本文基于 Kafka 2.2.1 版本.本文主要是以源码的手段一步一步探究消息发送流程,如果对源码不感兴趣,可以直接跳到文末查看消息发送流程图与消息发送本地缓存存储结构. 从上文 初识 Ka ...
随机推荐
- STS或eclipse安装SVN插件
安装sts--SVN插件 简介:sts是与eclipse类似的Java IDE开发工具(不了解的百度) 1.sts菜单栏 help->install New Software 依据大家的版本选择 ...
- 本博客已经迁移去http://blog.brightwang.com/
本博客已经迁移去http://blog.brightwang.com/ ,感谢各位支持.
- hibernate中.常见的hql查询语句
hql是非常有意识的被设计为完全面向对象的查询 基本规则: 1.hql语法类似于sql,但它后面跟的不是表名和字段名,而是类名和属性名 2.hql大小写不敏感.但是设计java类名,包名,属性名时大小 ...
- hive sql 随机抽样
create table daizk.IOS_matrix_sex asselect *from zhujx.1029_IOS_features_replce_nullwhere sex = 'M'u ...
- bootstrap中给表格设置display之后表格宽度变小问题解决
问题描述:bootstrap中给表格设置display之后表格宽度变小了 解决方案:给表格加上 display:table样式就可以了.
- selenium常用的断言
断言: 验证应用程序的状态是否同期望的一致,常见的断言包括验证页面内容,如标题是否与预期一致,当前的位置是否正确等等 断言常被用的4种模式+5种手段:Assert 断言失败的时候,该测试终止 veri ...
- python,使用PIL库对图片进行操作
在做识别验证码时,需要对验证码图片进行一些处理,所以就学习了一下PIL的知识,下面是我总结的一些常用方法. 注明:图片的操作都需要Image库,所以要使用import Image导入库 1.打开图片 ...
- php下的原生ajax请求
浏览器中为我们提供了一个JS对象XMLHttpRequet,它可以帮助我们发送HTTP请求,并接受服务端的响应. 意味着我们的浏览器不提交,通过JS就可以请求服务器. ajax(Asynchron ...
- php 账号不能同时登陆,当其它地方登陆时,当前账号失效
解决的思路是每当用户登陆时我们必需记录当前的用户id和session_id,如果有人在其它地方用此账号登陆时,我们把此用户id对应的session_id的session文件删除,并重新记录当前的ses ...
- 本地Maven环境配置
本地Maven环境配置 下载配置文件:http://10.1.10.138:6060/root/DevelopmentSpecification/archive/master.zip 解压master ...