码迷,mamicode.com
首页 > Web开发 > 详细

libcurl原理解析(2) - libcurl对poll的使用

时间:2015-07-13 18:37:11      阅读:632      评论:0      收藏:0      [点我收藏+]

标签:libcurl原理解析   libcurl源码分析   libcurl对poll的使用   poll   

libcurl同时封装了select以及poll这两种I/O机制。代码中使用宏HAVE_POLL_FINE对这两者进行分离。如果定义了这个宏,则使用poll,否则使用select。
这两者的使用代码都定义在函数curl_poll()中,而函数定义在文件lib/select.c中。为了方便分析,阅读,会将select与poll相关的代码分离开来,各自独立分析。
本篇文章主要分析curl_poll()中对poll的封装使用。
先看下使用到的一些数据结构的定义:

typedef int curl_socket_t ;
#define CURL_SOCKET_BAD -1
struct pollfd
{
        curl_socket_t fd;
        short   events;
        short   revents;
};
/*查阅了linux的man手册,对这三个成员的说明如下:
The field fd contains a file descriptor for an open file. If this field is negative, 
then the corresponding events field is ignored and the revents field returns zero. (This 
provides an easy way of ignoring a file descriptor for a single poll() call: 
simply negate the fd field.)

The field events is an input parameter, a bit mask specifying the events the application 
is interested in for the file descriptorfd. 
If this field is specified as zero, then all events are ignored for fd and revents returns zero.

The field revents is an output parameter, filled by the kernel with the events that 
actually occurred. The bits returned inrevents can include any of those specified in 
events, or one of the values POLLERR, POLLHUP, or POLLNVAL. (These three bits are meaningless 
in the events field, and will be set in the revents field whenever the corresponding condition is true.)
*/

下面是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 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()获取当前时间
       }

        do
       {
               if ( timeout_ms < 0)   //为负数,则poll无限等待
                     pending_ms = -1;
               else if (! timeout_ms)   //为0,则poll会立即返回,即使没有可用的events。
                     pending_ms = 0;

              r = poll( ufds, nfds, pending_ms);   //真正调用poll()
               if (r != -1)  //poll()调用成功,则跳出循环。
                      break;

               //poll调用失败,返回-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)
               if ( timeout_ms > 0)
              {
                     pending_ms = timeout_ms - elapsed_ms;
                      if (pending_ms <= 0)
                     {
                           r = 0;     //模拟poll超时的情形
                            break;
                     }
              }
       } while (r == -1);
       /*现在可以对上面的这个while循环总结一下:
       1.如果poll调用成功(即返回值>=0),则结束循环
       2.如果poll调用失败(即返回值==-1),则检测errno是否为EINTR错误。
       如果不是EINTR,则结束循环。
       如果是EINTR,则检测是否已经超时。超时则结束循环,没有超时则继续polling。
       */

        if (r < 0)    //poll()调用失败
               return -1;
        if (r == 0)   //poll()超时
               return 0;

        for (i = 0; i < nfds; i++)
       {
               if ( ufds[i].fd == CURL_SOCKET_BAD)
                      continue;
               if ( ufds[i].revents & POLLHUP)
                      ufds[i].revents |= POLLIN;              //fd仍然可能读
               if ( ufds[i].revents & POLLERR)
                      ufds[i].revents |= (POLLIN | POLLOUT);  //fd仍然可能读写
       }
       /*
       POLLERR,POLLHUP以及POLLNVAL的含义如下:
       POLLHUP意味着socket连接已经中断。 在TCP中,意味着已经接收到了FIN并且也已经发出去了。
       POLLERR意味着socket发生了一个异步错误。在TCP中,它通常表示已经接收到了一个RST,或者已经发出去了一个RST。如果fd不是一个socket,则POLLERR可能表示设备不支持polling.
       对于上述的这两种标识,fd可能处于open状态,还没有被关闭(但是shutdown()函数可能已经调用了)。
       POLLNVAL意味着fd是无效的,不代表任何已打开的文件。
       */

        return r;     //返回值。此时必定>0
}
这个函数执行完成后,第一个输入参数ufds中的成员可能会被修改。最后,函数返回poll的实际返回值。

版权声明:本文为博主原创文章,未经博主允许不得转载。

libcurl原理解析(2) - libcurl对poll的使用

标签:libcurl原理解析   libcurl源码分析   libcurl对poll的使用   poll   

原文地址:http://blog.csdn.net/shltsh/article/details/46865359

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!