select 订阅
select是一个计算机函数,位于头文件#include 。该函数用于监视文件描述符的变化情况——读写或是异常。 展开全文
select是一个计算机函数,位于头文件#include 。该函数用于监视文件描述符的变化情况——读写或是异常。
信息
外文名
select
头文件
#include
属    性
Linux 网络编程
返回值
>0、-1、0
select头文件
#include /* 根据POSIX.1 - 2001 *//*根据早期的标准*/#include#include#include
收起全文
精华内容
参与话题
问答
  • select函数详解

    千次阅读 2018-04-01 15:10:35
    select函数的功能和调用顺序 使用select函数可以完成非阻塞方式工作的程序,它能够监视我们需要监视的文件描述符的变化情况——读写或是异常。 非阻塞方式:non-block,就是进程或线程执行此函数时不必非要等待...

    select函数的功能和调用顺序

    使用select函数可以完成非阻塞方式工作的程序,它能够监视我们需要监视的文件描述符的变化情况——读写或是异常。
    非阻塞方式:non-block,就是进程或线程执行此函数时不必非要等待事件的发生,一旦执行肯定返回,以返回值的不同来反映函数的执行情况,如果事件发生则与阻塞方式相同,若事件没有发生,则返回一个代码来告知事件未发生,而进程或线程继续执行,所以效率较高。
    select函数用来统一监视多个文件描述符的:
    1、 是否存在套接字接收数据?
    2、 无需阻塞传输数据的套接字有哪些?
    3、 哪些套接字发生了异常?

    select函数调用过程:
    这里写图片描述
    由上图知,调用select函数需要一些准备工作,调用后还需要查看结果。

    设置文件描述符

    select可以同时监视多个文件描述符(套接字)。
    此时需要先将文件描述符集中到一起。集中时也要按照监视项(接收,传输,异常)进行区分,即按照上述3种监视项分成三类。
    使用fd_set数组变量执行此项操作,该数组是存有0和1的位数组。
    这里写图片描述
    数组是从下标0开始,最左端的位表示文件描述符0。如果该位值为1,则表示该文件描述符是监视对象。
    图上显然监视对象为fd1和fd3。

    “是否应当通过文件描述符的数字直接将值注册到fd_set变量?”
    当然不是!操作fd_set的值由如下宏来完成:

    FD_ZERO(fd_set* fdset): 将fd_set变量的所有位初始化为0。
    FD_SET(int fd, fd_set* fdset):在参数fd_set指向的变量中注册文件描述符fd的信息。
    FD_CLR(int fd, fd_set* fdset):参数fd_set指向的变量中清除文件描述符fd的信息。
    FD_ISSET(int fd, fd_set* fdset):若参数fd_set指向的变量中包含文件描述符fd的信息,则返回真。
    这里写图片描述

    设置监视范围及超时

    select函数:

    #include <sys/select.h>
    #include <sys/time.h>
    int select(int maxfd, fd_set* readset, fd_set* writeset, fd_set* exceptset, 
    const struct timeval* timeout);

    select函数共有5个参数,其中参数和返回值:
    maxfd:监视对象文件描述符数量。
    readset:将所有关注“是否存在待读取数据”的文件描述符注册到fd_set变量,并传递其地址值。
    writeset: 将所有关注“是否可传输无阻塞数据”的文件描述符注册到fd_set变量,并传递其地址值。
    exceptset:将所有关注“是否发生异常”的文件描述符注册到fd_set变量,并传递其地址值。
    timeout:调用select后,为防止陷入无限阻塞状态,传递超时信息。
    返回值:错误返回-1,超时返回0。当关注的事件返回时,返回大于0的值,该值是发生事件的文件描述符数。

    select函数用来验证3种监视项的变化情况。根据监视项声明3个fd_set变量,分别向其注册文件描述符信息,并把变量的地址传递到函数的第二到第四个参数。但是,在调用select函数前需要决定2件事:
    “文件描述符的监视范围是?”
    “如何设定select函数的超时时间?”

    第一,文件描述符的监视范围与第一个参数有关。实际上,select函数要求通过第一个参数传递监视对象文件描述符的数量。因此,需要得到注册在fd_set变量中的文件描述符数。但每次新建文件描述符时,其值都会增1,故只需将最大的文件描述符值加1再传递到select函数即可。(加1是因为文件描述符的值从0开始)
    第二,超时时间与最后一个参数有关。其中timeval结构体如下:

    struct timeval
    {
        long tv_sec;
        long tv_usec;
    };

    本来select函数只有在监视文件描述符发生变化时才返回,未发生变化会进入阻塞状态。指定超时时间就是为了防止这种情况发生。
    将上述结构体填入时间值,然后将结构体地址值传给select函数的最后一个参数,此时,即使文件描述符中未发生变化,只要过了指定时间,也可以从函数返回。不过这种情况下,select函数返回0。 不想设置超时最后一个参数只需要传递NULL。

    调用select函数后查看结果

    如果select返回值大于0,说明文件描述符发生了变化。

    关于文件描述符变化:
    文件描述符变化是指监视的文件描述符中发生了相应的监视事件。
    例如通过select的第二个参数传递的集合中存在需要读取数据的描述符时,就意味着文件描述符发生变化。

    怎样获知哪些文件描述符发生了变化?向select函数的第二到第四个参数传递的fd_set变量中将产生变化,如下图:
    这里写图片描述
    select函数调用完成后,向其传递的fd_set变量中将发生变化。原来为1的所有位均变为0,但发生变化的文件描述符对应位除外。因此,可以认为值为1的位置上的文件描述符发生了变化。

    select函数调用实例

    #include <stdio.h>
    #include <unistd.h>
    #include <sys/time.h>
    #include <sys/select.h>
    
    #define BUF_SIZE 30
    
    int main(int argc, char* argv[])
    {
        fd_set reads,temps;
        int result, str_len;
        char buf[BUF_SIZE];
        struct timeval timeout;
        FD_ZERO(&reads);
        FD_SET(0, &reads);//监视文件描述符0的变化, 即标准输入的变化
        /*超时不能在此设置!
        因为调用select后,结构体timeval的成员tv_sec和tv_usec的值将被替换为超时前剩余时间.
        调用select函数前,每次都需要初始化timeval结构体变量.
        timeout.tv_sec = 5;
        timeout.tv_usec = 5000;*/
        while(1)
        {
            /*将准备好的fd_set变量reads的内容复制到temps变量,因为调用select函数后,除了发生变化的fd对应位外,
            剩下的所有位都将初始化为0,为了记住初始值,必须经过这种复制过程。*/
            temps = reads;
            //设置超时
            timeout.tv_sec = 5;
            timeout.tv_usec = 0;
    
            //调用select函数. 若有控制台输入数据,则返回大于0的整数,如果没有输入数据而引发超时,返回0.
            result = select(1, &temps, 0, 0, &timeout);
            if(result == -1)
            {
                perror("select() error");
                break;
            }
            else if(result == 0)
            {
                puts("timeout");
            }
            else
            {
                //读取数据并输出
                if(FD_ISSET(0, &temps))
                {
                    str_len = read(0, buf, BUF_SIZE);
                    buf[str_len] = 0;
                    printf("message from console: %s", buf);
                }
            }
        }
    
        return 0;
    }

    程序运行结果:

    nihao
    message from console: nihao
    goodbye
    message from console: goodbye
    timeout
    timeout

    转载自:
    https://blog.csdn.net/y396397735/article/details/55004775

    展开全文
  • select用法&原理详解(源码剖析)

    万次阅读 多人点赞 2018-04-02 20:11:53
    最近刚接触Linux下的select用法,查阅了很多资料终于懂得了一丁点,故将自己查阅后有用的资料整理在这下面。博客链接都是很有价值,写的很好的文章。在研读源码时主要看的是这篇文章:深入select多路复用内核源码加...

    最近刚接触Linux下的select用法,查阅了很多资料终于懂得了一丁点,故将自己查阅后有用的资料整理在这下面。博客链接都是很有价值,写的很好的文章。在研读源码时主要看的是这篇文章:深入select多路复用内核源码加驱动实现 自己能力精力有限,没有办法自己写一篇完完整整的文章,故只能当个搬运工了,文章先后顺序尽量按照了知识点的先后~~如果有什么问题欢迎一起探讨学习~


    前期知识

    在开始接触select之前,你需要先对IO的同步,异步,阻塞,非阻塞有个基本的了解,知道什么是IO多路复用。下面这篇文章可以帮助你快速区分这几种模型:IO - 同步,异步,阻塞,非阻塞 (亡羊补牢篇)

    要了解select,你必须得先接触过socket编程,了解什么是文件描述符(fd),文件描述符表,文件指针,可以参阅下面这三篇博文: Linux的SOCKET编程详解 Linux下 文件描述符(fd)与 文件指针(FILE*) file结构体详解

    select的使用场景:

    select需要使用两个system call (select 和 recvfrom),而blocking IO只调用了一个system call (recvfrom)。但是,用select的优势在于它可以同时处理多个connection。(多说一句。所以,如果处理的连接数不是很高的话,使用select/epoll的web server不一定比使用multi-threading + blocking IO的web server性能更好,可能延迟还更大。select/epoll的优势并不是对于单个连接能处理得更快,而是在于能处理更多的连接。)
    在IO multiplexing Model中,实际中,对于每一个socket,一般都设置成为non-blocking,但是,如上图所示,整个用户的process其实是一直被block的。只不过process是被select这个函数block,而不是被socket IO给block。


    select用法

    select的函数格式:

    int select(int maxfdp,fd_set *readfds,fd_set *writefds,fd_set *errorfds,struct timeval *timeout);

    解释一下各个参数的意思
    1. int maxfdp是一个整数值,是指集合中所有文件描述符的范围,即所有文件描述符的最大值加1,不能错!在Windows中这个参数的值无所谓,可以设置不正确。

    2. struct fd_set可以理解为一个集合,这个集合中存放的是文件描述符(file descriptor),即文件句柄。fd_set集合可以通过一些宏由人为来操作。

      FD_ZERO(fd_set *fdset):清空fdset与所有文件句柄的联系。 
      FD_SET(int fd, fd_set *fdset):建立文件句柄fd与fdset的联系。 
      FD_CLR(int fd, fd_set *fdset):清除文件句柄fd与fdset的联系。 
      FD_ISSET(int fd, fdset *fdset):检查fdset联系的文件句柄fd是否可读写,>0表示可读写。 
    3. struct timeval用来代表时间值,有两个成员,一个是秒数,另一个是毫秒数。 若将NULL以形参传入,即不传入时间结构,就是将select置于阻塞状态,一定等到监视文件描述符集合中某个文件描述符发生变化为止;第二,若将时间值设为0秒0毫秒,就变成一个纯粹的非阻塞函数,不管文件描述符是否有变化,都立刻返回继续执行,文件无变化返回0,有变化返回一个正值;第三,timeout的值大于0,这就是等待的超时时间,即select在timeout时间内阻塞,超时时间之内有事件到来就返回了,否则在超时后不管怎样一定返回。

      struct timeval{      
      
              long tv_sec;   /*秒 */
      
              long tv_usec;  /*微秒 */   
      
          }
    4. 三个fd_set分别监视文件描述符的读写异常变化,如果有select会返回一个大于0的值。如果没有则在timeout的时间后select返回0,若发生错误返回负值。可以传入NULL值,表示不关心任何文件的读/写/异常变化。

    参考:linux中select()函数分析

    为什么linux select函数的第一个参数总应该是fdmax + 1

    表示的是文件描述符的数量,从0开始所以比最大的描述符多1,详情参考博客: 为什么linux select函数的第一个参数总应该是fdmax + 1 ?——poll和epoll不需要+1


    简单理解select模型

    理解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,0011

    (4)执行select(6,&set,0,0,0)阻塞等待

    (5)若fd=1,fd=2上都发生可读事件,则select返回,此时set变为0000,0011。注意:没有事件发生的fd=5被清空。

    所以,我们可以得到select模型的特点:
    (1) 文件描述符个数有限,一般来说这个数目和系统内存关系很大。select使用位域的方式来传递关心的文件描述符,位域就有最大长度。select使用位域的方式传回就绪的文件描述符,调用者需要循环遍历每一个位判断是否就绪,当文件描述符个数很多,但是空闲的文件描述符大大多于就绪的文件描述符的时候,效率很低。

    (2) 将fd加入select监控集的同时,还要再使用一个数据结构array保存放到select监控集中的fd,一是用于再select 返回后,array作为源数据和fd_set进行FD_ISSET判断。二是select返回后会把以前加入的但并无事件发生的fd清空,则每次开始 select前都要重新从array取得fd逐一加入(FD_ZERO最先),扫描array的同时取得fd最大值maxfd,用于select的第一个 参数。

    (3) 可见select模型必须在select前循环array(加fd,取maxfd),select返回后循环array(FD_ISSET判断是否有时间发生)。

    参考:
    Linux中select IO复用机制&使用代码实例
    select处理带外数据&解决socket中多用户问题代码


    Linux的socket 事件wakeup callback机制

    Linux通过socket睡眠队列来管理所有等待socket的某个事件的process,同时通过wakeup机制来异步唤醒整个睡眠队列上等待事件的process,通知process相关事件发生。通常情况,socket的事件发生的时候,其会顺序遍历socket睡眠队列上的每个process节点,调用每个process节点挂载的callback函数。在遍历的过程中,如果遇到某个节点是排他的,那么就终止遍历,总体上会涉及两大逻辑:(1)睡眠等待逻辑;(2)唤醒逻辑。

    参考:大话 Select、Poll、Epoll


    select源码分析

    函数调用图

    asmlinkage long sys_select(int n, fd_set __user *inp, fd_set __user *outp, fd_set __user *exp, struct timeval __user *tvp) {
        //从用户进程拷贝超时时间,将超时时间换成jiffies。
        //使用转化后的时间调用ret = core_sys_select(n, inp, outp, exp, &timeout)函数
        //将剩余时间拷贝回用户空间进程
    }
    staticint core_sys_select(int n, fd_set __user *inp, fd_set __user *outp, fd_set __user *exp, s64 *timeout) {
        //读取当前进程的文件描述符表,如果传入的n大于当前进程最大的文件描述符,给予修正。
        //尝试使用栈分配内存,不够则用堆。需要使用6倍于最大描述符的描述符个数
        //get_fd_set调用copy_from_user从用户空间拷贝了fd_set
        //执行ret = do_select(n, &fds, timeout);
        //将修改后的fd_set写回用户空间
    }

    do_select函数中,遍历所有n个fd,对每一个fd调用对应驱动程序中的poll函数。poll函数调用poll_wait函数,poll_wait函数调用__pollwait(),这个函数会初始化等待队列项(有个pollwake函数),并将该等待队列项添加到从驱动程序中传递过来的等待队列头中去。驱动程序在得知设备有IO事件时(通常是该设备上IO事件中断),会调用wakeup,wakeup –> _wake_up_common -> curr->func(即pollwake)。pollwake函数里面调用_pollwake函数, 通过pwq->triggered = 1将进程标志为唤醒。再调用default_wake_function(&dummy_wait, mode, sync, key)这个默认的通用唤醒函数唤醒调用select的进程。 请注意,poll函数会返回一个mask码值,通过这个值我们可以判断是否可读写。更详细的必须看 do_select源码。

    参考:
    分析源码时看的是这篇博客
    Linux内核select源码剖析
    这篇比较简单


    为什么 select 慢

    1. 在第一次所有监听都没有事件时,调用 select 都需要把进程挂到所有监听的文件描述符一次。

    2. 有事件到来时,不知道是哪些文件描述符有数据可以读写,需要把所有的文件描述符都轮询一遍才能知道。

    3. 通知事件到来给用户进程,需要把整个 bitmap 拷到用户空间,让用户空间去查询。


    有用的扩展链接:

    socket、端口、进程的关系

    从glibc源码看系统调用原理

    堆区和栈区内存分配区别


    可能的问题

    在上面的简单理解select模型中,我觉得表述可能有点问题,fd_set的大小是1024,那么能表示的应该是0-1023才对。个人觉得,如果fd = 5的话,可能是在第六位。具体怎样才是正确得之后有机会试一下。

    参考:一种linux下扩展select模型管理能力的方法

    展开全文
  • SQL中SELECT语句详解

    万次阅读 多人点赞 2018-06-04 17:12:25
    本篇文章讲述SQL语句中的SELECT查询语句,以供参考,如有错误或不当之处还望大神们告知。 简单查询SELECT-FROM 用于无条件查询单张表中的行或列 假设有表如图所示 查询名字叫 ‘叶清逸’ 的记录: select...

    本篇文章讲述SQL语句中的SELECT查询语句,以供参考,如有错误或不当之处还望大神们告知。

    简单查询SELECT-FROM

    用于无条件查询单张表中的行或列

    假设有表如图所示
    这里写图片描述

    • 查询名字叫 ‘叶清逸’ 的记录:
    select * from T_USER where u_name = '叶清逸' ;

    查询结果:
    这里写图片描述

    • 查询一个或多个属性,u_name,u_age,u_score:
    select u_name,u_age,u_score from T_USER ;

    查询结果:
    这里写图片描述

    AS关键字

    • 使用AS给查询结果取别名(AS也可以省略):
    --可加""也可不加 如果不加双引号默认字母大写 如果需要小写字母或别名由多个字母组成则需要加上双引号。
    --AS也可省略。
    select 
    u_name as 名字,
    u_age as "年龄" , 
    u_score 成绩
    from T_USER ;

    查询结果:
    这里写图片描述

    算数运算符

    • SELECT中可以对数字和日期进行加减乘除运算
    select u_name , u_score , u_score+10 , u_score-10 , u_score*2 , u_score/2 
    from T_USER ;

    运行结果:
    这里写图片描述

    连接符 “||”

    SQL中的连接符”||” 可将结果连接起来为一列
    - 将u_name 和“的成绩为” u_score 连接起来

    select 
    u_name || '的成绩为' || u_score as 成绩
    from T_USER ;

    查询结果:
    这里写图片描述

    条件查询SELECT-WHERE

    SQL中可以用SELECT-WHERE进行条件查询

    比较运算 ‘<’ ‘<=’ ‘>’ ‘>=’ ‘!=’

    SQL中可以使用 条件运算符 ‘<’ ‘<=’ ‘>’ ‘>=’ ‘!=’ 过滤查询结果中的某些行,多个条件之间可以用 ‘and’ 连接。
    - 查询T_USER表中成绩大于等于60分的学生

    select u_name , u_score
    from T_USER 
    where u_score >= 60 ;

    查询结果:
    这里写图片描述

    与条件 (AND,BETWEEN)

    查询A-B区间

    • 查询T_USER表中成绩大于等于60分且小于等于80分的学生
    • AND写法
    select u_name , u_score
    from T_USER 
    where u_score >60 and u_score <80
    • BETWEEN写法
    select u_name , u_score
    from T_USER 
    where u_score between 60 and 80 ;

    查询结果:
    这里写图片描述

    或条件(OR,IN)

    SQL中用于或运算的关键字OR和IN

    • 查询分数大于90分或者小于60分的记录-OR
    select u_name , u_score
    from T_USER 
    where u_score >90 or u_score <60 ;

    运行结果:
    这里写图片描述
    - 查询成绩为100,98,65的学生-IN

    select u_name , u_score
    from T_USER 
    where u_score in(100,98,65) ;

    运行结果:
    这里写图片描述

    非条件(!=,NOT)

    • 查询名字不叫 ‘叶清逸’ 的记录
    • !=写法
    select *
    from T_USER 
    where u_name != '叶清逸' ;
    • NOT写法
    select *
    from T_USER 
    where u_name not like '叶清逸' ;

    查询结果:
    这里写图片描述

    模糊查询LIKE

    SQL中可以使用模糊查询like,其中 ‘_’ 占一位 ‘%’占多位。

    • 查询名字中有 ‘清’ 字的记录
    • 可以有三种表示方式,结果等价
      1. 包含’清’: %清%
      2. 第二位为’清’:_清%
      3. 倒数第二位为’清’:%清_
    select *
    from T_USER 
    where u_name like '%清%' ;

    查询结果:
    这里写图片描述
    注:若要查询数据含有’_’ 关键字 则需使用 escape定义一个转义字符。如:like ‘%#_%’ escape ‘#’

    查询排序ORDER BY

    SQL中可以对查询结果进行排序,DESC表示按从大到小排序,ASC表示按从小到大排序,默认为从小到大排序。
    - 将T_USER表按成绩从大到小排序

    select *
    from T_USER 
    order by u_score desc ;

    查询结果:
    这里写图片描述

    ORDER BY 语句也可以用逗号隔开,表示如果上一个条件相同,接下来的排序方式。

    展开全文
  • select()函数的作用

    万次阅读 2018-08-16 17:43:01
    select()在SOCKET编程中还是比较重要的,可是对于初学SOCKET的人来说都不太爱用select()写程序,他们只是习惯写诸如 conncet()、accept()、recv()或recvfrom()这样的阻塞程序(所谓阻塞方式block,顾名思义,就是进程...

    select()在SOCKET编程中还是比较重要的,可是对于初学SOCKET的人来说都不太爱用select()写程序,他们只是习惯写诸如 conncet()、accept()、recv()或recvfrom()这样的阻塞程序(所谓阻塞方式block,顾名思义,就是进程或是线程执行到这些函数时必须等待某个事件发生,如果事件没有发生,进程或线程就被阻塞,函数不能立即返回)。可是使用select()就可以完成非阻塞(所谓非阻塞方式non-block,就是进程或线程执行此函数时不必非要等待事件的发生,一旦执行肯定返回,以返回值的不同来反映函数的执行情况。如果事件发生则与阻塞方式相同,若事件没有发生则返回一个代码来告知事件未发生,而进程或线程继续执行,所以效率高)方式工作的程序,它能够监视我们需要监视的文件描述符的变化情况——读写或是异常。下面详细介绍一下!

      select()函数的格式(我所说的是Unix系统下的Berkeley Socket编程,和Windows下的有区别,一会儿说明):

     

      int select(int maxfdp, fd_set* readfds, fd_set* writefds, fd_set* errorfds, struct timeval* timeout);

     

      先说明两个结构体:

     

      第一:struct fd_set可以理解为一个集合,这个集合中存放的是文件描述符(file descriptor),即文件句柄,这可以是我们所说的普通意义的文件,当然Unix下任何设备、管道、FIFO等都是文件形式,全部包括在内,所以,毫无疑问,一个socket就是一个文件,socket句柄就是一个文件描述符。fd_set集合可以通过一些宏由人为来操作,比如清空集合:FD_ZERO(fd_set*),将一个给定的文件描述符加入集合之中FD_SET(int, fd_set*),将一个给定的文件描述符从集合中删除FD_CLR(int,   fd_set*),检查集合中指定的文件描述符是否可以读写FD_ISSET(int, fd_set*)。一会儿举例说明。

     

      第二:struct timeval是一个大家常用的结构,用来代表时间值,有两个成员,一个是秒数,另一个毫秒数。

     

      具体解释select的参数:

     

      int maxfdp是一个整数值,是指集合中所有文件描述符的范围,即所有文件描述符的最大值加1,不能错!在Windows中这个参数值无所谓,可以设置不正确。

     

      fd_set* readfds是指向fd_set结构的指针,这个集合中应该包括文件描述符,我们是要监视这些文件描述符的读变化的,即我们关心是否可以从这些文件中读取数据了,如果这个集合中有一个文件可读,select就会返回一个大于0的值,表示有文件可读,如果没有可读的文件,则根据timeout参数再判断是否超时,若超出timeout的时间,select返回0,若发生错误返回负值。可以传入NULL值,表示不关心任何文件的读变化。

     

      fd_set* writefds是指向fd_set结构的指针,这个集合中应该包括文件描述符,我们是要监视这些文件描述符的写变化的,即我们关心是否可以向这些文件中写入数据了,如果这个集合中有一个文件可写,select就会返回一个大于0的值,表示有文件可写,如果没有可写的文件,则根据timeout再判断是否超时,若超出timeout的时间,select返回0,若发生错误返回负值。可以传入NULL值,表示不关心任何文件的写变化。

     

      fe_set* errorfds同上面两个参数的意图,用来监视文件错误异常。

     

      struct timeval* timeout是select的超时时间,这个参数至关重要,它可以使select处于三种状态。

     

      第一:若将NULL以形参传入,即不传入时间结构,就是将select置于阻塞状态,一定等到监视文件描述符集合中某个文件描述符发生变化为止;

     

      第二:若将时间值设为0秒0毫秒,就变成一个纯粹的非阻塞函数,不管文件描述符是否有变化,都立刻返回继续执行,文件无变化返回0,有变化返回一个正值;

     

      第三:timeout的值大于0,这就是等待的超时时间,即select在timeout时间内阻塞,超时时间之内有事件到来就返回了,否则在超时后不管怎样一定返回,返回值同上述。

     

      返回值:

     

      负值:select错误

     

      正值:某些文件可读写或出错

     

      0:等待超时,没有可读写或错误的文件

     

      在有了select后可以写出像样的网络程序来!举个简单的例子,就是从网络上接受数据写入一个文件中。

     

    ---------------------------无连接    

    例子:

      int main()

      {

          int sock;

          FILE* fp;

          struct fd_set fds;

          struct timeval timeout = {3, 0}; //select 等待3秒,3秒轮询, 要非阻塞就置0

          char buffer[256] = {0}; //256字节的接收缓冲区

          /*假设已经建立UDP连接,具体过程不写,简单,当然TCP也同理,主机ip和port都已经给定,要写的文件已经打开

          sock = socket(...);

          bind(...);

          fp = fopen(...); */

          while(1)

          {

              FD_ZERO(&fds); //每次循环都要清空,否则不能检测描述符变化

              FD_SET(sock, &fds); //添加描述符

              FD_SET(fp, &fds); //同上

              maxfdp = sock>fp?sock+1:fp+1; //描述符最大值加1

              switch(select(maxfdp, &fds, &fds, NULL, &timeout)) //select使用

              {

                  case SOCKET_ERROR: exit(-1); break; //select错误,退出程序

                  case 0: break; //再次轮询

                  default:

                      if(FD_ISSET(sock, &fds)) //测试sock是否可读,即是否网络上有数据

                      {

                          recvfrom(sock, buffer, 256, .... ); //接受网络数据

                           if(FD_ISSET(fp, &fds)) //测试文件是否可写

                          fwrite(fp, buffer...); //写入文件

                          buffer清空;

                      } //end if break

              } //end switch

          } //end while

      } //end main

     

    ---------------------------面向连接

       #include <winsock.h>

       #include <stdio.h>

       #define PORT       5150

       #define MSGSIZE     1024

       #pragma comment(lib, "ws2_32.lib")

       int     g_iTotalConn = 0;

       SOCKET g_CliSocketArr[FD_SETSIZE];

       DWORD WINAPI WorkerThread(LPVOID lpParameter);

       int main()

       {   

           WSADATA     wsaData;   

           SOCKET       sListen, sClient;   

           SOCKADDR_IN local, client;   

           int         iaddrSize = sizeof(SOCKADDR_IN);   

           DWORD       dwThreadId;   

           // Initialize Windows socket library   

           WSAStartup(0x0202, &wsaData);   

           // Create listening socket   

           sListen = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);   

           // Bind           

           local.sin_addr.S_un.S_addr = htonl(INADDR_ANY);

           local.sin_family = AF_INET;

           local.sin_port = htons(PORT);   

           bind(sListen, (struct sockaddr *)&local, sizeof(SOCKADDR_IN));   

           // Listen   listen(sListen, 3);   

           // Create worker thread   

           CreateThread(NULL, 0, WorkerThread, NULL, 0, &dwThreadId);     

           while (TRUE)   

           {               // Accept a connection     

               sClient = accept(sListen, (struct sockaddr *)&client, &iaddrSize);     

               printf("Accepted client:%s:%d\n", inet_ntoa(client.sin_addr), ntohs(client.sin_port));     

               // Add socket to g_CliSocketArr     

               g_CliSocketArr[g_iTotalConn++] = sClient;   

           }     

           return 0;

       }

       DWORD WINAPI WorkerThread(LPVOID lpParam)

       {   

           int             i;   

           fd_set         fdread;   

           int             ret;   

           struct timeval tv = {1, 0};   

           char           szMessage[MSGSIZE];     

           while (TRUE)   

           {     

               FD_ZERO(&fdread);     

               for (i = 0; i < g_iTotalConn; i++)

               {

                   FD_SET(g_CliSocketArr, &fdread);

               }                     // We only care read event

               ret = select(0, &fdread, NULL, NULL, &tv);

               if (ret == 0)

               {       // Time expired

                   continue;

               }

               for (i = 0; i < g_iTotalConn; i++)

               {

                   if (FD_ISSET(g_CliSocketArr, &fdread))

                     {         // A read event happened on g_CliSocketArr

                         ret = recv(g_CliSocketArr, szMessage, MSGSIZE, 0);

                         if (ret == 0 || (ret == SOCKET_ERROR && WSAGetLastError() == WSAECONNRESET))

                           {

                               // Client socket closed           

                               printf("Client socket %d closed.\n", g_CliSocketArr);

                               closesocket(g_CliSocketArr);

                               if (i < g_iTotalConn - 1)

                               {

                                   g_CliSocketArr[i--] = g_CliSocketArr[--g_iTotalConn];

                               }

                           }

                           else

                           {

                                 // We received a message from client

                                 szMessage[ret] = '\0';

                                 send(g_CliSocketArr, szMessage, strlen(szMessage), 0);

                           }

                     } //if

               }//for

           }//while     

           return 0;

       }

       服务器的几个主要动作如下:

       1.创建监听套接字,绑定,监听;

       2.创建工作者线程;

       3.创建一个套接字数组,用来存放当前所有活动的客户端套接字,每accept一个连接就更新一次数组;

       4.接受客户端的连接。

       这里有一点需要注意的,就是我没有重新定义FD_SETSIZE宏,所以服务器最多支持的并发连接数为64。而且,这里决不能无条件的ccept,服务器应该根据当前的连接数来决定

    是否接受来自某个客户端的连接。一种比较好的实现方案就是采用WSAAccept函数,而且让WSAAccept回调自己实现的Condition Function。

       如下所示:

       int CALLBACK ConditionFunc(LPWSABUF lpCallerId,LPWSABUF lpCallerData, LPQOS lpSQOS,LPQOS lpGQOS,LPWSABUF lpCalleeId, LPWSABUF lpCalleeData,GROUP FAR *

    g,DWORD dwCallbackData)

       {

           if (当前连接数 < FD_SETSIZE)

               return CF_ACCEPT;

           else   

               return CF_REJECT;

       }

       工作者线程里面是一个死循环,一次循环完成的动作是:

       1.将当前所有的客户端套接字加入到读集fdread中;

       2.调用select函数;

       3.查看某个套接字是否仍然处于读集中,如果是,则接收数据。如果接收的数据长度为0,或者发生WSAECONNRESET错误,则表示客户端套接字主动关闭,这时需要将服务器中对应的套接字所绑定的资源释放掉,然后调整我们的套接字数组(将数组中最后一个套接字挪到当前的位置上)。

       除了需要有条件接受客户端的连接外,还需要在连接数为0的情形下做特殊处理,因为如果读集中没有任何套接字,select函数会立刻返回,这将导致工作者线程成为一个毫无停顿的死循环,CPU的占用率马上达到100%。

       关系到套接字列表的操作都需要使用循环,在轮询的时候,需要遍历一次,再新的一轮开始时,将列表加入队列又需要遍历一次.也就是说,Select在工作一次时,需要至少遍历2次列表,这是它效率较低的原因之一.

       在大规模的网络连接方面,还是推荐使用IOCP或EPOLL模型.但是Select模型可以使用在诸如对战类游戏上,比如类似星际这种,因为它小巧易于实现,且对战类游戏的网络连接量并不大. 对于Select模型想要突破Windows 64个限制的话,可以采取分段轮询,一次轮询64个.例如套接字列表为128个,在第一次轮询时,将前64个放入队列中用Select进行状态查询,待本次操作全部结束后.将后64个再加入轮询队列中进行轮询处理.这样处理需要在非阻塞式下工作.以此类推,Select也能支持无限多个.

     

    原文:https://blog.csdn.net/zhubosa/article/details/44729247

    展开全文
  • 细谈select函数(C语言)

    万次阅读 多人点赞 2010-11-06 16:50:00
    Select在Socket编程中还是比较重要的,可是对于初学Socket的人来说都不太爱用Select写程序,他们只是习惯写诸如connect、accept、recv或recvfrom这样的阻塞程序(所谓阻塞方式block,顾名思义,就是进程或是线程执行...
  • 为什么不建议用select * ?

    千次阅读 2019-08-22 14:41:16
    select * from table; “经验”丰富后,可能你看到了这样的SQL后,大脑中本能的搜索出了网上的各种说使用*不好的文章,但具体是为啥不好呢? 业务表象原因: 1 ,有这样的场景,一个表T被A,B两个功能使用,两...
  • select

    2014-11-17 15:37:51
    作用: 函数参数解析:
  • select 0 与select (0)

    2018-01-16 15:53:00
    SELECT(0),是一个函数 返回当前工作区的编号 SELECT 0 是一个命令 选定未被使用的且最小的可以使用的工作区为当前工作区 转载于:https://www.cnblogs.com/alpf/p/8297091.html...
  • SELECT

    2019-06-20 08:40:00
    SELECT 基本结构 --语句顺序 SELECT COLUNM_NAME --COLUNM_NAME 字段名 FROM TABLE_NAME --表名 WHERE --限制条件 GROUP BY --分组 HAVING --对分组后的结果进行筛选 ORDER BY ...
  • select函数详解及实例分析

    万次阅读 多人点赞 2012-10-22 09:56:14
    Select函数在Socket编程中还是比较重要的,可是对于初学Socket的人来说都不太爱用Select写程序,他们只是习惯写诸如connect、 accept、recv或recvfrom这样的阻塞程序(所谓阻塞方式block,顾名思义,就是进程或是...
  • select函数

    千次阅读 2018-08-03 21:31:24
    select函数:  系统提供select函数来实现多路复用输入/输出模型。原型:  #include &lt;sys/time.h&gt;  #include &lt;unistd.h&gt;  select函数:  系统提供select函数来实现多路复用...
  • select函数的详细使用(C语言)

    万次阅读 2018-03-17 11:08:36
    可是对于初学Socket的人来说都不太爱用Select写程序,他们只是习惯写诸如connect、accept、recv或recvfrom这样的阻塞程序(所谓阻塞方式block,顾名思义,就是进程或是线程执行到这些函数时必须等待某个事件的发生,...
  • linux select函数详解

    万次阅读 多人点赞 2013-10-08 08:54:35
    函数实现I/O端口的复用,传递给 select 函数的参数会告诉内核:  • 我们所关心的文件描述符  • 对每个描述符,我们所关心的状态。(我们是要想从一个文件描述符中读或者写,还是关注一个描述符中是否出现异常)  ...
  • select函数的使用

    千次阅读 2019-04-04 14:04:27
    select函数的作用: select()在SOCKET编程中还是比较重要的,可是对于初学SOCKET的人来说都不太爱用select()写程序,他们只是习惯写诸如 conncet()、accept()、recv()或recvfrom这样的阻塞程序(所谓阻塞方式block,...
  • linux之select函数用法详解

    万次阅读 2016-01-03 10:23:27
    select系统调用是用来让我们的程序监视多个文件句柄(file ...文件句柄在Linux里很多,如果你man某个函数,在函数返回值部分说到成功后有一个文件句柄被创建的都是,如man socket可以看到“On success, a file desc
  • Linux中select函数及实例

    千次阅读 2017-11-27 15:07:05
    Unix中的函数select和poll用来,支持Unix中I/O复用的功能,在Unix中I/O模型可以分为以一几种: (1)阻塞I/O (2)非阻塞I/O (3)I/O复用(select和poll) (4)信号驱动I/O(SIGIO) (5)异步I/O  ...
  • Windows Socket select函数使用

    万次阅读 2014-03-12 20:28:36
    最近做一个通信服务程序,读取数据时在工作线程中使用Socket 的select方式进行...最后认真的查了select函数的说明,才发现一些问题。 1,函数原型:  int select(int nfds, fd_set* readfds, fd_set* write
  • C语言中select函数简介及使用

    千次阅读 2019-09-14 20:27:40
    select函数用来检查套接字描述符(sockets descriptors)是否已准备好读/写,提供了一种同时检查多个套接字的方法。 Linux中select函数的声明在/usr/include/x86_64-linux-gnu/sys/select.h文件中,Windows下select...
  • select函数作用

    千次阅读 2010-05-22 22:24:00
    可是对于初学Socket的人来说都不太爱用Select写程序,他们只是习惯写诸如connect、accept、recv或recvfrom这样的阻塞程序(所谓阻塞方式block,顾名思义,就是进程或是线程执行到这些函数时必须等待某个事件的发生,...
  • IO多路复用之select函数详解

    千次阅读 2016-08-17 00:48:14
    IO复用  我们首先来看看服务器编程的模型,客户端发来的请求服务端会产生一个进程来对其进行服务,每当来一... 也就是说IO复用的“介质”是进程(准确的说复用的是select和poll,因为进程也是靠调用select和poll来实
  • select函数详细用法解析

    万次阅读 2016-10-24 15:39:24
    int select(int n,fd_set * readfds,fd_set * writefds,fd_set * exceptfds,struct timeval * timeout); 3.函数说明 select()用来等待文件描述词状态的改变。参数n代表最大的文件描述词加1,参数r
  • TCP/IP编程之select函数详解

    千次阅读 2016-07-12 00:14:11
    linux下的I/O复用模型目前很多都已经不用select函数了,而是用epoll,但是为什么还需要了解select编程呢,其实是从两个方面考虑的:一是为了通过select去理解epoll,而是非阻塞的connect函数也会用到select函数。...
  • select函数与I/O多路转接

    千次阅读 热门讨论 2007-08-02 13:48:00
    <!-- @page { size: 21cm 29.7cm; margin: 2cm } P { margin-bottom: 0.21cm } --> select函数与I/O多路转接 相作大家都写过读写IO操作的代码,例如从socket中读取数据可以使用如下的代码: while( (n = read(so
  • C++网络编程Select函数用法

    千次阅读 2017-03-10 15:35:54
    可是对于初学Socket的人来说都不太爱用Select写程序,他们只是习惯写诸如 connect、accept、recv或recvfrom这样的阻塞程序(所谓阻塞方式block,顾名思义,就是进程或是线程执行到这些函数时必须等 待某个事件的发生...
  • Linux C的select函数的使用

    千次阅读 2015-08-31 22:57:14
    1、select函数简介int select(int maxfdp,fd_set *readfds,fd_set *writefds,fd_set *errorfds,struct timeval*timeout); 当服务器响应多个客户端连接的时候,需要定义一个线程函数,在每一个线程函数里面处理该连接...
  • Linux-C网络编程之select函数

    千次阅读 2015-08-06 05:15:19
    开门见山,如果我们要对多个客户端连接的多个事件进行操作,首先会想到建立多个线程或进程让其去各自进行,这也是最简单的模式。 但对每一个线程或进程而言,无论连接是否有事件发生,都必须随时待命,也就是说,...
  • 总体上来说select函数的作用: 确定一个或多个套接口的状态,本函数用于确定一个或多个套接口的状态,对每一个套接口,调用者可查询它的可读性、可写性及错误状态信息,用fd_set结构来表示一组等待检查的套接口,在...
  • select函数何时返回?

    千次阅读 2014-03-13 00:10:33
    先运行一下如下代码: #include #include // winsock接口 #pragma comment(lib, "ws2_32.lib") // winsock实现 int main() { WORD wVersionRequested; // 双字节,winsock库的版本 WSADATA wsaData;...
  • C语言中select函数的使用

    千次阅读 2018-10-29 20:25:56
    但是使用select函数可以实现非阻塞方式的程序。它能够监视我们需要监视的文件描述符的变化情况——读写或是异常。 Select的函数格式: int select(int maxfdp,fd_set *readfds,fd_set *writefds,fd_...
  • select函数用法详解(以例子剖析)

    千次阅读 2014-06-06 20:33:31
    /* * This example opens a console and a serial port for * read mode, and calls select() with a 5 second timeout. * It waits for data to be available on either descriptor. */ #inclu

空空如也

1 2 3 4 5 ... 20
收藏数 3,292,145
精华内容 1,316,858
关键字:

select