libcurl实现解析(3) - libcurl对select的使用
1.前言
在本系列的前一篇文章中。介绍了libcurl对poll()的使用。
參考"libcurl原理解析(2) - libcurl对poll的使用"。
本篇文章主要分析curl_poll()中对select()的封装使用。与前一篇类似,我们仅仅分离出与select相关的代码。
2.curl_poll函数分析
这个函数中使用到的一些其他的数据结构,能够參考前一篇文章中的介绍。本篇不再介绍。
/*
这个函数是对poll()的封装。假设poll()不存在,则使用select()替代。
假设使用的是select(),而且文件描写叙述符fd太大,超过了FD_SETSIZE,则返回error。
假设传入的timeout值是一个负数。则会无限的等待。直到没有有效的fd被提供。 当发生
这样的情况(没有有效的fd)时。则负数timeout值会被忽略,且函数会马上超时。 返回值:
-1 = 系统调用错误或fd>=FD_SETSIZE.
0 = timeout.
N = 返回的pollfd结构体的个数,且当中的revents成员不为0.
*/
int Curl_poll(struct pollfd ufds[], unsigned int nfds, int timeout_ms)
{
struct timeval pending_tv;
struct timeval *ptimeout;
fd_set fds_read;
fd_set fds_write;
fd_set fds_err;
curl_socket_t maxfd; struct timeval initial_tv = { 0, 0 };
bool fds_none = TRUE; //用于验证传入的ufds数组是否有效
unsigned int i;
int pending_ms = 0;
int error; //保存错误码
int r; //检測全部fd中是否存在有效的fd。
//假设至少存在一个有效的fd,则fds_none置为false。停止检測
if (ufds)
{
for (i = 0; i < nfds; i++)
{
if (ufds[i].fd != CURL_SOCKET_BAD)
{
fds_none = FALSE;
break;
}
}
} //假设全部的fd都是无效的(即bad socket, -1)。则等待一段时间后。直接返回。
if (fds_none)
{
r = Curl_wait_ms(timeout_ms); //此函数会随后进行分析
return r;
} //当传入的timeout值是一个负数(堵塞情形)或者0时。则无需衡量elapsed time.
//否则,获取当前时间。 if (timeout_ms > 0)
{
pending_ms = timeout_ms;
initial_tv = curlx_tvnow();//调用gettimeofday()或time()获取当前时间
} //每次调用select()前都须要又一次初始化fdset,由于它们既是输入參数又是输出參数。
FD_ZERO(&fds_read);
FD_ZERO(&fds_write);
FD_ZERO(&fds_err);
maxfd = (curl_socket_t)-1; for (i = 0; i < nfds; i++)
{
ufds[i].revents = 0;
if (ufds[i].fd == CURL_SOCKET_BAD) //跳过无效的fd
continue;
VERIFY_SOCK(ufds[i].fd); //检測是否0<=fd<FD_SETSIZE.超出这个范围。则返回-1. if (ufds[i].events & (POLLIN | POLLOUT | POLLPRI |
POLLRDNORM | POLLWRNORM | POLLRDBAND))
{
if (ufds[i].fd > maxfd) //获取到最大的fd,做为select()的第一个參数。
maxfd = ufds[i].fd;
if (ufds[i].events & (POLLRDNORM | POLLIN))
FD_SET(ufds[i].fd, &fds_read);
if (ufds[i].events & (POLLWRNORM | POLLOUT))
FD_SET(ufds[i].fd, &fds_write);
if (ufds[i].events & (POLLRDBAND | POLLPRI))
FD_SET(ufds[i].fd, &fds_err);
}
} //做为select()的timeout參数
ptimeout = (timeout_ms < 0) ? NULL : &pending_tv; do
{
if (timeout_ms > 0)
{
pending_tv.tv_sec = pending_ms / 1000;
pending_tv.tv_usec = (pending_ms % 1000) * 1000;
}
else if (!timeout_ms)
{
pending_tv.tv_sec = 0;
pending_tv.tv_usec = 0;
} //真正调用select(). 第2。3,4參数已经在前面初始化(清空)过了。
r = select((int)maxfd + 1, &fds_read, &fds_write, &fds_err, ptimeout);
if (r != -1) //select调用成功,结束循环
break; //select调用失败。返回-1。通过errno能够获取到错误码。 error = SOCKERRNO; //宏定义。 #define SOCKERRNO (errno) //以下的error_not_EINTR 是宏定义.
//#define error_not_EINTR (0 || error != EINTR)
if (error && error_not_EINTR) //检測是否存在error,且不是EINTR错误
break; //没有出错或者存在EINTR错误。则推断是否继续运行select()
if (timeout_ms > 0)
{
//elapsed_ms是宏定义。
//#define elapsed_ms (int)curlx_tvdiff(curlx_tvnow(), initial_tv)
pending_ms = timeout_ms - elapsed_ms;
if (pending_ms <= 0)
{
r = 0; //模拟select超时的情形
break;
}
}
} while (r == -1);
/*如今能够对上面的这个while循环总结一下:
1.假设select调用成功(即返回值>=0),则结束循环
2.假设select调用失败(即返回值==-1),则检測errno是否为EINTR错误。
假设不是EINTR,则结束循环。
假设是EINTR,则检測是否已经超时。超时则结束循环,没有超时则继续select()。
*/ if (r < 0) //select()调用失败
return -1;
if (r == 0) //select()超时
return 0; //select()调用成功, 统计当中状态发生改变的fd的个数,保存至r.
r = 0;
for (i = 0; i < nfds; i++)
{
ufds[i].revents = 0;
if (ufds[i].fd == CURL_SOCKET_BAD)
continue;
if (FD_ISSET(ufds[i].fd, &fds_read)) //fd可读
ufds[i].revents |= POLLIN;
if (FD_ISSET(ufds[i].fd, &fds_write)) //fd可写
ufds[i].revents |= POLLOUT;
if (FD_ISSET(ufds[i].fd, &fds_err)) //fd出错
ufds[i].revents |= POLLPRI;
if (ufds[i].revents != 0)
r++;
} return r;
}
这个函数运行完毕后。第一个输入參数ufds中的成员revents可能会被改动。最后。函数返回select()的实际返回值。
3.curl_wait_ms函数分析
以下是curl_wait_ms()函数的详细实现。
也是基于poll或者select来实现的。这里也仅仅讨论它的select()实现版本号。
/*
这个函数用于等待特定的时间值。在函数Curl_socket_ready()以及Curl_poll()中被调用。
当没有提供不论什么fd来检測时。则仅仅是等待特定的一段时间。 假设是在windows平台下,则winsock中的poll()以及select()超时机制,须要一个有效的socket fd.
这个函数不同意无限等待,假设传入的值是0或者负数。则马上返回。
超时时间的精度以及最大值。取决于系统。 返回值:
-1 = 系统调用错误,或无效的输入值(timeout),或被中断。
0 = 指定的时间已经超时
*/
int Curl_wait_ms(int timeout_ms)
{
struct timeval pending_tv;
struct timeval initial_tv;
int pending_ms;
int error;
int r = 0; if (!timeout_ms) //超时值为0,马上返回
return 0;
if (timeout_ms < 0) //不能为负数
{
SET_SOCKERRNO(EINVAL);
return -1;
} pending_ms = timeout_ms;
initial_tv = curlx_tvnow();
do
{
pending_tv.tv_sec = pending_ms / 1000;
pending_tv.tv_usec = (pending_ms % 1000) * 1000; r = select(0, NULL, NULL, NULL, &pending_tv); if (r != -1) //select()调用成功,则跳出循环
break; //select调用失败。返回-1。通过errno能够获取到错误码。
error = SOCKERRNO; //宏定义。 #define SOCKERRNO (errno) //以下的error_not_EINTR 是宏定义. #define error_not_EINTR (0 || error != EINTR)
if (error && error_not_EINTR) ////检測是否存在error,且不是EINTR错误
break; //elapsed_ms是宏定义:
//#define elapsed_ms (int)curlx_tvdiff(curlx_tvnow(), initial_tv)
pending_ms = timeout_ms - elapsed_ms;
if (pending_ms <= 0)
{
r = 0; //模拟select超时的情形
break;
}
} while (r == -1); //确保返回值r仅仅能为-1(超时失败)或者0(超时成功)。 //r不可能大于0,由于传入到select()函数的3个fdset数组所有都是NULL。假设select的返回值>0,则说明调用出问题了。
//故这里会将r置为-1,即调用超时失败。
if (r)
r = -1;
return r;
}
libcurl实现解析(3) - libcurl对select的使用的更多相关文章
- jquery动态刷新select的值,后台传过来List<T>,前台解析后填充到select的option中
jquery动态刷新select的值:将后台传来的List<T>赋值到select下的option. 第一个select选择后出发该方法refreshMerchant(params),传递 ...
- Mybaits 源码解析 (七)----- Select 语句的执行过程分析(下篇)(Mapper方法是如何调用到XML中的SQL的?)全网最详细,没有之一
我们上篇文章讲到了查询方法里面的doQuery方法,这里面就是调用JDBC的API了,其中的逻辑比较复杂,我们这边文章来讲,先看看我们上篇文章分析的地方 SimpleExecutor public & ...
- linux c libcurl的简单使用(转)
curl是Linux下一个非常著名的下载库,通过这个库,可以很简单的实现文件的下载等操作.看一个简单的例子: #include <curl/curl.h> #include <std ...
- Libcurl细说
libcurl教程 原文地址:http://curl.haxx.se/libcurl/c/libcurl-tutorial.html 译者:JGood(http://blog.csdn.net/J ...
- libcurl教程
名称 libcurl 的编程教程 目标 本文档介绍使用libcurl编程的一般原则和一些基本方法.本文主要是介绍 c 语言的调用接口,同时也可能很好的适用于其他类 c 语言的接口. 跨平台的可移植代码 ...
- C++ 用libcurl库进行http通讯网络编程
使用libcurl完成http通讯,很方便而且是线程安全,转载一篇比较好的入门文章 转载自 http://www.cnblogs.com/moodlxs/archive/2012/10/15/2724 ...
- C++ 用libcurl库进行http通讯网络编程(转)
转载:http://www.cnblogs.com/moodlxs/archive/2012/10/15/2724318.html 目录索引: 一.LibCurl基本编程框架 二.一些基本的函数 三. ...
- cocos2dx libcurl
转自:http://www.himigame.com/curl-libcurl/878.html 本篇介绍使用libcurl编程的一般原则和一些基本方法.本文主要是介绍 c 语言的调用接口,同时也可能 ...
- libcurl
一.LibCurl基本编程框架 二.一些基本的函数 三.curl_easy_setopt函数部分选项介绍 四.curl_easy_perform 函数说明(error 状态码) 五.libcurl使用 ...
随机推荐
- Google广告屏蔽插件adBlock
今天在博客园写博客的时候发现莫名其妙的在右侧被植入了广告,询问了管理员得知存在以下几种可能: 1.电信网络供应商劫持网页,植入广告 2.ADSafe(是一款去除广告的软件,效果很不错) 但经过最终排除 ...
- React Native 环境搭建踩坑
React Native (web Android)环境搭建踩坑(真的是一个艰辛的过程,大概所有坑都被我踩了 官方文档地址 : https://facebook.github.io/react-nat ...
- Python 时间处理---------笔记
时区处理&格式化 import pytz from datetime import datetime # 设置时区 timezone = pytz.timezone('Asia/Shangha ...
- Linux 配置JDK + MyEclipse
版本:Ubuntu16.04: jdK: Java SE Development Kit 8u102; My Eclipse: 10.6; JDK配置的细致步骤参见此处. 就一点要注意: 请使用代码进 ...
- What is the difference between Gradle Build and Gradle Sync?
Gradle Build helps you to compile your Android app into an APK while Gradle Sync will sync up all yo ...
- unzip 命令巧用举例
1.把文件解压到当前目录下 unzip master.zip 2.如果要把文件解压到指定的目录下,需要用到-d参数. unzip -d /tmp master.zip 3.解压的时候,有时候不想覆盖已 ...
- php连接数据库的两种方式
一.mysqli方式连接数据库 $mysql_conf = array( 'host' => 'localhost:3306', 'db' => 'ssql', 'db_user' =&g ...
- 所有对象的父类(java.lang.Object)
一.介绍 Object类是类层次结构的根源,每一个类都存在一个父类为Object类.所有的对象,包括数组,都实现了 Object 类的方法. 二.对象初始化 这里使用了静态代码块进行Object类的初 ...
- jquery spa
1.hashchange监听 2.根据url加载不同页面 $.ajax({ url:"/xx/xx.html" type:"get", dataType:&qu ...
- Linux之网络文件共享服务(FTP)
一.FTP概念 •File Transfer Protocol 早期的三个应用级协议之一 •基于C/S结构 •双通道协议:数据和命令连接 •数据传输格式:二进制(默认)和文本 •两种模式:服务器角度 ...