zoukankan      html  css  js  c++  java
  • select(),fd_set(),fd_isset()

    1. select函数

    1. 用途

       在编程的过程中,经常会遇到许多阻塞的函数,好像read和网络编程时使用的recv, recvfrom函数都是阻塞的函数,当函数不能成功执行的时候,程序就会一直阻塞在这里,无法执行下面的代码。这时就需要用到非阻塞的编程方式,使用select函数就可以实现非阻塞编程。
           select函数是一个轮循函数,循环询问文件节点,可设置超时时间,超时时间到了就跳过代码继续往下执行。

    2. 大致原理

      select需要驱动程序的支持,驱动程序实现fops内的poll函数。select通过每个设备文件对应的poll函数提供的信息判断当前是否有资源可用(如可读或写),如果有的话则返回可用资源的文件描述符个数,没有的话则睡眠,等待有资源变为可用时再被唤醒继续执行。详细的原理请看这里

    3. 函数定义

    该函数声明如下:

    int select(int nfds,  fd_set* readset,  fd_set* writeset,  fe_set* exceptset,  struct timeval* timeout);
    

    参数:

           nfds           需要检查的文件描述字个数
           readset     用来检查可读性的一组文件描述字。
           writeset     用来检查可写性的一组文件描述字。
           exceptset  用来检查是否有异常条件出现的文件描述字。(注:错误不包括在异常条件之内)
           timeout      超时,填NULL为阻塞,填0为非阻塞,其他为一段超时时间

    返回值:

           返回fd的总数,错误时返回SOCKET_ERROR

    2. fd_set结构体

    上面select函数中需要用到两个fd_set形参,这个结构体到底做什么用的呢?

     fd_set其实这是一个数组的宏定义,实际上是一long类型的数组,每一个数组元素都能与一打开的文件句柄(socket、文件、管道、设备等)建立联系,建立联系的工作由程序员完成,当调用select()时,由内核根据IO状态修改fd_set的内容,由此来通知执行了select()的进程哪个句柄可读。

    系统提供了FD_SETFD_CLRFD_ISSETFD_ZERO进行操作,声明如下:

    FD_SET(int fd, fd_set *fdset);       //将fd加入set集合
    FD_CLR(int fd, fd_set *fdset);       //将fd从set集合中清除
    FD_ISSET(int fd, fd_set *fdset);     //检测fd是否在set集合中,不在则返回0
    FD_ZERO(fd_set *fdset);              //将set清零使集合中不含任何fd

      下面写一段程序探究一下这几个宏的工作:

    #include <WINSOCK2.H> 
    int main()
    {   
        fd_set fdset;   
        FD_ZERO(&fdset);   
        FD_SET(1, &fdset);   
        FD_SET(2, &fdset);   
        FD_SET(3, &fdset);   
        FD_SET(7, &fdset);   
        int isset = FD_ISSET(3, &fdset);   
        printf("isset = %d
    ", isset);   
        FD_CLR(3, &fdset);   
        isset = FD_ISSET(3, &fdset);   
        printf("isset = %d
    ", isset);   
        return 0;
    }

       当使用FD_SET添加完1、2、3、7后,fdset的值如下:

    图1. 引自https://www.cnblogs.com/wuyepeng/p/9745573.html

    然后经过FD_CLR以后,fd_array[2]就被清除了,数组后面的数据依次往前提,即7被放到了fd_array[2]

     所以isset前后两次打印的值分别为1和0。

    3. 小结

      select的结果会对fd_set造成影响。例如,对于一个监听的socket:

    #include <WinSock2.h>
    #include <stdio.h>
    #pragma comment(lib,"WS2_32.lib")   
    int main()
    {  
        FD_SET   ReadSet;  
        FD_ZERO(&ReadSet); 
        WSADATA   wsaData; 
        WSAStartup(MAKEWORD(2, 2), &wsaData);         //初始化
        SOCKET  ListenSocket = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);  //定义一个监听套接字    
        //bind等操作这里省略....   //.....    
        FD_SET(ListenSocket, &ReadSet);      //将套接字加入ReadSet集合中    
        int isset = FD_ISSET(ListenSocket, &ReadSet);         //这里并没有通过select对fd_set进行筛选   
        printf("Before select, isset = %d
    ", isset);         //所以这里打印结果为1 
        struct timeval tTime;  
        tTime.tv_sec = 10; 
        tTime.tv_usec = 0; 
        select(0, &ReadSet, NULL, NULL, &tTime);       //通过select筛选处于就绪状态的fd                                                  
        //这时,刚才的ListenSocket并不在就绪状态(没有连接连入),那么就从ReadSet中去除它    
        isset = FD_ISSET(ListenSocket, &ReadSet);  
        printf("After select, isset = %d
    ", isset);     //所以这里打印的结果为0 
        system("pause");   
        return 0;
    }

      所以可以使用select以及fd的操作来完成异步的网络消息处理,具体的实现请看这里的例子

    Over...

    转自:

    1、select函数及fd_set介绍

  • 相关阅读:
    EnjoyCSS – 在线的,先进的 CSS3 代码生成器
    使用 Google Analytics 跟踪 JavaScript 错误
    免费 PSD 素材:25个全新的界面设计资源
    Framework7 – 赞!功能齐全的 iOS7 App 前端框架
    CutJS – 用于 HTML5 游戏开发的 2D 渲染引擎
    【干货分享】Node.js 中文资料导航
    酷站设计:2014年3月份获奖网站作品欣赏
    Magic CSS3 – 创建各种神奇的交互动画效果
    优秀示例:一组创意的手机注册和登录界面设计
    酷站设计!15个扁平风格网站作品欣赏
  • 原文地址:https://www.cnblogs.com/gjmhome/p/13985714.html
Copyright © 2011-2022 走看看