zoukankan      html  css  js  c++  java
  • select()

    从别人的博客中转载过来了这一篇文章,经过重新编辑排版之后展现于此,做一个知识点保存与学习。
        select函数用于在非阻塞中,当一个套接字或一组套接字有信号时通知你,系统提供select函数来实现多路复用输入/输出模型,原型:
    int select(int maxfd,fd_set *rdset,fd_set *wrset,fd_set *exset,struct timeval *timeout);
    
       所在的头文件为:#include <sys/time.h> 和#include <unistd.h>
    
       先对函数中的参数做一个简单的介绍。参数maxfd是需要监视的最大的文件描述符值+1;rdset,wrset,exset分别对应于需要检测的可读文件描述符的集合,可写文件描述符的集合及异常文件描述符的集合。struct timeval结构用于描述一段时间长度,如果在这个时间内,需要监视的描述符没有事件发生则函数返回,返回值为0。 
        在这些参数中有一个类似于结构体的东西,fd_set,这是什么的名字,我们先来看看这个所具有的含义吧。这是一组文件描述字(fd)的集合,它用一位来表示一个fd,等等,文件描述字,熟悉吧,之前都把这个当做一个文件的路径保存的地方了,也就是当做是一个文件的标志哦,现不在做猜想了,看看下文是怎么介绍的吧。
    
        对于fd_set类型通过下面四个宏来操作:
        FD_ZERO(fd_set *fdset) 将指定的文件描述符集清空,在对文件描述符集合进行设置前,必须对其进行初始化,如果不清空,由于在系统分配内存空间后,通常并不作清空处理,所以结果是不可知的。
        FD_SET(fd_set *fdset) 用于在文件描述符集合中增加一个新的文件描述符。
        FD_CLR(fd_set *fdset) 用于在文件描述符集合中删除一个文件描述符。
        FD_ISSET(int fd,fd_set *fdset) 用于测试指定的文件描述符是否在该集合中。
    
    
        过去。。。。。。好长一大段哦,为了保证大家的注意力,我决定将这一大段长长的对过去情况的介绍去掉,直接寻找正题,保持目标的关注度啊。现在,UNIX系统通常会在头文件<sys/select.h>中定义常量FD_SETSIZE,它是数据类型fd_set的描述字数量,其值通常是1024,这样就能表示<1024的fd。
    
    
        好了在研究了一番关于fd_set的信息之后,再回到对select函数的理解上来吧。
    
        功能:测试指定的fd可读?可写?有异常条件待处理?
        readset  用来检查可读性的一组文件描述字。
        writeset 用来检查可写性的一组文件描述字。
        exceptset用来检查是否有异常条件出现的文件描述字。(注:不包括错误)
        timeout  用于描述一段时间长度,如果在这个时间内,需要监视的描述符没有事件发生则函数返回,返回值为0。
    
        对于select函数的功能简单的说就是对文件fd做一个测试。测试结果有三种可能:
        1.timeout=NULL(阻塞:select将一直被阻塞,直到某个文件描述符上发生了事件)
        2.timeout所指向的结构设为非零时间(等待固定时间:如果在指定的时间段里有事件发生或者时间耗尽,函数均返回)
        3.timeout所指向的结构,时间设为0(非阻塞:仅检测描述符集合的状态,然后立即返回,并不等待外部事件的发生)
    
     
    
        返回值:返回对应位仍然为1的fd的总数。注意啦:只有那些可读,可写以及有异常条件待处理的fd位仍然为1。否则为0哦。举个例子,比如recv(), 在没有数据到来调用它的时候,你的线程将被阻塞,如果数据一直不来,你的线程就要阻塞很久.这样显然不好。所以采用select来查看套节字是否可读(也就是是否有数据读了) 。
    步骤如下——
    socket s;
    .....
    fd_set set;
    while(1)
    {
    FD_ZERO(&set);//将你的套节字集合清空
    FD_SET(s, &set);//加入你感兴趣的套节字到集合,这里是一个读数据的套节字s
    select(0,&set,NULL,NULL,NULL);//检查套节字是否可读,
    //很多情况下就是是否有数据(注意,只是说很多情况)
    //这里select是否出错没有写
    if(FD_ISSET(s, &set) //检查s是否在这个集合里面,
    { //select将更新这个集合,把其中不可读的套节字去掉
    //只保留符合条件的套节字在这个集合里面
    recv(s,...);
    }
    //do something here
    }
    
        理解select模型的关键在于理解fd_set,为说明方便,取fd_set长度为1字节,fd_set中的每一bit可以对应一个文件描述符fd。则1字节长的fd_set最大可以对应8个fd。
       (1)执行fd_set set; FD_ZERO(&set);则set用位表示是0000,0000。
       (2)若fd=5,执行FD_SET(fd,&set);后set变为0001,0000(第5位置为1)
    
       (3)若再加入fd=2,fd=1,则set变为0001,00114)执行select(6,&set,0,0,0)阻塞等待
       (5)若fd=1,fd=2上都发生可读事件,则select返回,此时set变为0000,0011。注意:没有事件发生的fd=5被清空。
    
    
    基于上面的讨论,可以轻松得出select模型的特点:
       (1)可监控的文件描述符个数取决与sizeof(fd_set)的值。
    
       (2)可以有效突破select可监控的文件描述符上限。
       (3)将fd加入select监控集的同时,还要再使用一个数据结构array保存放到select监控集中的fd,一是用于再select 返回后,array作为源数据和fd_set进行FD_ISSET判断。二是select返回后会把以前加入的但并无事件发生的fd清空,则每次开始 select前都要重新从array取得fd逐一加入(FD_ZERO最先),扫描array的同时取得fd最大值maxfd,用于select的第一个参数。
       (4)可见select模型必须在select前循环array(加fd,取maxfd),select返回后循环array(FD_ISSET判断是否有时间发生)。
    
    
    使用select函数的过程一般是:
        先调用宏FD_ZERO将指定的fd_set清零,然后调用宏FD_SET将需要测试的fd加入fd_set,接着调用函数select测试fd_set中的所有fd,最后用宏FD_ISSET检查某个fd在函数select调用后,相应位是否仍然为1。
    
    以下是一个测试单个文件描述字可读性的例子:
    int isready(int fd)
    {
    int rc;
    fd_set fds;
    struct tim tv;
    FD_ZERO(&fds);
    FD_SET(fd,&fds);
    tv.tv_sec = tv.tv_usec = 0;
    rc = select(fd+1, &fds, NULL, NULL, &tv);
    if (rc < 0) //error
    return -1;
    return FD_ISSET(fd,&fds) ? 1 : 0;
    }
     
  • 相关阅读:
    防火墙透明模式
    HP管理工具System Management Homepage安装配置
    kbmmw 中JSON 中使用SQL 查询
    kbmmw 中JSON 操作入门
    第一个kbmmw for Linux 服务器
    kbmmw 5.02发布
    kbmmw 5.01 发布
    使用delphi 10.2 开发linux 上的Daemon
    使用unidac 在linux 上无驱动直接访问MS SQL SERVER
    使用delphi 10.2 开发linux 上的webservice
  • 原文地址:https://www.cnblogs.com/zzyoucan/p/3955578.html
Copyright © 2011-2022 走看看