select()函数以及FD_ZERO、FD_SET、FD_CLR、FD_ISSET

合集下载

linux Select函数用法详解

linux  Select函数用法详解

Socket-SelectSelect在Socket编程中还是比较重要的,可是对于初学Socket的人来说都不太爱用Select写程序,他们只是习惯写诸如 connect、accept、recv或recvfrom 这样的阻塞程序(所谓阻塞方式block,顾名思义,就是进程或是线程执行到这些函数时必须等待某个事件的发生,如果事件没有发生,进程或线程就被阻塞,函数不能立即返回)。

可是使用Select就可以完成非阻塞(所谓非阻塞方式non-block,就是进程或线程执行此函数时不必非要等待事件的发生,一旦执行肯定返回,以返回值的不同来反映函数的执行情况,如果事件发生则与阻塞方式相同,若事件没有发生则返回一个代码来告知事件未发生,而进程或线程继续执行,所以效率较高)方式工作的程序,它能够监视我们需要监视的文件描述符的变化情况——读写或是异常。

下面详细介绍一下。

Select的函数格式: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* )。

select函数的返回值

select函数的返回值

select函数的返回值select函数是一种多路复用函数,它的作用是在多个文件描述符上等待IO事件发生。

具体来说,它可以在一组文件描述符上等待事件,这些事件可以是读、写或错误事件。

当其中任何一个文件描述符上发生了与它所感兴趣的事件相对应的事件,select函数就会返回。

这个返回值可以告诉我们发生了什么事件,从而让我们做出相应的处理。

select函数的返回值包含三个集合,分别是读、写和错误事件的文件描述符集合。

这些集合都是用结构体fd_set来表示的。

fd_set结构体定义如下:```c++typedef struct fd_set {unsigned long fds_bits[FD_SETSIZE / (8 * sizeof(unsigned long))];} fd_set;```其中,FD_SETSIZE 是一个预定义的常量,它表示一个 fd_set 中最多可以包含多少个文件描述符。

每个文件描述符都可以被 fd_set 中的一个 bit 位表示,fds_bits 数组就是用来保存这些位的。

在 select 函数调用完成后,我们可以通过对这三个集合进行操作,来获取文件描述符对应的事件是否发生。

具体来说,我们可以使用以下三个宏来操作 fd_set 结构体:- FD_ISSET(fd, &fdset):检查 fd 是否在 fdset 集合内,如果是则返回非 0 值,否则返回 0。

- FD_SET(fd, &fdset):将 fd 加入到 fdset 集合内。

- FD_CLR(fd, &fdset):将 fd 从 fdset 集合中移除。

例如,如果我们希望检查文件描述符 fd 是否发生读事件,就可以调用 FD_ISSET 函数,传入读事件的集合对应的 fd_set 结构体。

select函数的返回值是一个int类型的整数,其含义与返回的三个集合密切相关。

具体来说,它的取值有以下三种情况:- 如果返回值为负数,则表示 select 调用出错,具体的错误码可以通过 errno 来获取。

linux 中select的基本用法

linux 中select的基本用法

linux 中select的基本用法深入了解Linux 中select 的基本用法导语:在Linux 中,select 是一个重要的系统调用,用于同时监视多个文件描述符的可读、可写和出错条件。

它是实现多路复用IO的一种常用方法,能够帮助提高系统的性能。

本文将介绍select 的基本用法,从基础概念到具体使用方法,一步一步讲解,帮助读者更好地理解和应用该系统调用。

第一部分:基础知识1. select 的定义和作用- select 是一个系统调用,用于在一组文件描述符上进行IO 复用- 它能够同时监视多个文件描述符,并在有可读、可写或出错事件发生时进行相应的处理- 使用select 可以减少系统资源的浪费,提高系统的性能2. 文件描述符- 在Linux 中,文件和设备都通过文件描述符来操作- 文件描述符是一个非负整数,用于标识一个打开的文件或设备- 标准输入、标准输出和标准错误输出的文件描述符分别为0、1 和23. fd_set 类型- fd_set 是用于表示文件描述符集合的数据类型- 它通过一个位图来表示文件描述符集合的状态,每个文件描述符占用一个位- 可以使用宏函数来操作fd_set,如FD_ZERO、FD_SET、FD_CLR 和FD_ISSET第二部分:使用步骤1. 初始化文件描述符集合- 使用FD_ZERO 宏函数将文件描述符集合清零- 使用FD_SET 宏函数将需要监视的文件描述符添加到集合中2. 设置超时时间- 声明并初始化timeval 结构体变量,用于指定select 的超时时间- 如果不需要设置超时时间,则将timeval 结构体中的字段都设为03. 调用select 函数- 调用select 函数并传入最大文件描述符数、可读文件描述符集合、可写文件描述符集合、出错文件描述符集合和超时时间- select 函数会阻塞进程,直到有事件发生或超时,返回时会修改文件描述符集合的状态4. 处理事件- 使用FD_ISSET 宏函数判断特定文件描述符是否在集合中- 根据返回的文件描述符集合的状态,进行相应的读、写或出错操作第三部分:注意事项和高级用法1. 最大文件描述符数- select 的第一个参数需要指定最大文件描述符数加1- 如果文件描述符超过了该值,在一些旧版本的Linux 中可能会导致select 函数调用失败2. 文件描述符集合的修改- 在调用select 函数后,文件描述符集合的状态会被修改,只保留了就绪的文件描述符- 因此,每次调用select 函数前都需要重新初始化文件描述符集合3. 非阻塞模式和异步通知- select 在默认情况下是阻塞的,即会一直等待事件发生- 可以通过将文件描述符设置为非阻塞模式来改变这一行为,使得select立即返回- 也可以使用其他方法,如信号处理和管道通信,实现异步通知机制4. poll 和epoll 函数- poll 和epoll 是select 的替代方案,可以更好地处理大量文件描述符- 它们采用事件驱动的方式,不需要每次都重新初始化文件描述符集合- 在需要处理大量并发连接的情况下,可以考虑使用poll 或epoll 函数结语:本文从基础知识到具体使用步骤,详细介绍了Linux 中select 的基本用法。

select 用法 -回复

select 用法 -回复

select 用法-回复如何正确使用select函数在编程语言中,select函数被广泛用于处理多个输入/输出(I/O)操作。

它提供了一种有效的方式来同时监视多个通信通道,如文件描述符、套接字等,并在其中任意一个通道可进行I/O操作时进行相应处理。

本文将一步一步回答如何正确使用select函数。

第一步:了解select函数的基本概念和用法select函数是一种在非阻塞I/O操作中等待多个文件描述符就绪的方法。

它接受三个参数,第一个参数是整数值nfds,表示待监视的最大文件描述符值加1;第二个参数是fd_set类型的指针readfds,用于设置待监视的文件描述符集合;第三个参数是fd_set类型的指针writefds和exceptfds,用于设置待监视的文件描述符的特殊条件集合。

第二步:理解fd_set类型fd_set类型是一种用于表示文件描述符集合的数据类型。

它是一种位图(bitmap),每个文件描述符都对应着fd_set中的一位。

可以使用宏函数FD_ZERO、FD_SET、FD_CLR和FD_ISSET来操作fd_set类型,分别用于将fd_set清空、向fd_set中添加文件描述符、从fd_set中删除文件描述符以及判断某个文件描述符是否在fd_set中。

第三步:设置待监视的文件描述符集合在调用select函数之前,需要先设置待监视的文件描述符集合。

可以使用FD_ZERO和FD_SET宏函数来清空和向fd_set中添加文件描述符。

例如,假设我们要同时监视文件描述符1和2,可以如下设置:fd_set readfds;FD_ZERO(&readfds);FD_SET(1, &readfds);FD_SET(2, &readfds);第四步:调用select函数进行监视设置完待监视的文件描述符集合后,就可以调用select函数进行监视。

调用select函数后,它将阻塞等待,直到集合中至少一个文件描述符可进行I/O操作。

fdset参数一个用于检查可读性(readfds)[指南]

fdset参数一个用于检查可读性(readfds)[指南]

fd_set参数:一个用于检查可读性(readfds),一个用于检查可写性(writefds),另一个用于例外数据(exceptfds)。

从根本上说,fd_set数据类型代表着一系列特定套接字的集合。

其中,readfds集合包括符合下述任何一个条件的套接字:■有数据可以读入。

■连接已经关闭、重设或中止。

■假如已调用了listen,而且一个连接正在建立,那么accept函数调用会成功。

writefds集合包括符合下述任何一个条件的套接字:■有数据可以发出。

■如果已完成了对一个非锁定连接调用的处理,连接就会成功。

最后,exceptfds集合包括符合下述任何一个条件的套接字:■假如已完成了对一个非锁定连接调用的处理,连接尝试就会失败。

■有带外(Out-of-band,OOB)数据可供读取。

用select对套接字进行监视之前,在自己的应用程序中,必须将套接字句柄分配给一个集合,设置好一个或全部读、写以及例外fd_set结构。

将一个套接字分配给任何一个集合后,再来调用select,便可知道一个套接字上是否正在发生上述的I/O活动。

Winsock提供了下列宏操作,可用来针对I/O活动,对fd_set进行处理与检查:■FD_CLR(s,*set):从set中删除套接字s。

■FD_ISSET(s,*set):检查s是否set集合的一名成员;如答案是肯定的是,则返回TRUE。

■FD_SET(s,*set):将套接字s加入集合set。

■FD_ZERO(*set):将set初始化成空集合。

例如,假定我们想知道是否可从一个套接字中安全地读取数据,同时不会陷于无休止的“锁定”状态,便可使用FD_SET宏,将自己的套接字分配给fd_read集合,再来调用select。

要想检测自己的套接字是否仍属fd_read集合的一部分,可使用FD_ISSET宏。

采用下述步骤,便可完成用select操作一个或多个套接字句柄的全过程:1)使用FD_ZERO宏,初始化自己感兴趣的每一个fd_set。

Linux socket select 函数用法详解

Linux socket select 函数用法详解

linux 的socket函数分为阻塞和非阻塞两种方式,比如accept函数,在阻塞模式下,它会一直等待有客户连接。

而在非阻塞情况下,会立刻返回。

我们一般都希望程序能够运行在非阻塞模式下。

一种方法就是做一个死循环,不断去查询各个socket的状态,但是这样会浪费大量的cpu时间。

解决这个问题的一个方法就是使用select函数。

使用select函数可以以非阻塞的方式和多个socket通信。

当有socket需要处理时,select函数立刻返回,期间并不会占用cpu时间。

例程分析:#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <errno.h>#include <string.h>#include <sys/types.h>#include <sys/socket.h>#include <netinet/in.h>#include <arpa/inet.h>#define MYPORT 1234 // 侦听端口#define BACKLOG 5 // 最大可连接客户端数量#define BUF_SIZE 200int fd_A[BACKLOG]; // 连接的FD数组int conn_amount; // 当前连接的数量void showclient(){int i;printf("client amount: %d\n", conn_amount);for (i = 0; i < BACKLOG; i++){printf("[%d]:%d ", i, fd_A[i]);}printf("\n\n");}int main(void){int sock_fd, new_fd; // 侦听sock_fd, 新连接new_fdstruct sockaddr_in server_addr; // server address informationstruct sockaddr_in client_addr; // connector's address informationsocklen_t sin_size;int yes = 1;char buf[BUF_SIZE];int ret;int i;//创建侦听Socketif ((sock_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1){perror("Create listening socket error!");exit(1);}//配置侦听Socket//SO_REUSEADDR BOOL 允许套接口和一个已在使用中的地址捆绑。

select函数用法

select函数用法

Select在Socket编程中还是比较重要的,可是对于初学Socket的人来说都不太爱用Select写程序,他们只是习惯写诸如connect、accept、recv或recvfrom这样的阻塞程序(所谓阻塞方式block,顾名思义,就是进程或是线程执行到这些函数时必须等待某个事件的发生,如果事件没有发生,进程或线程就被阻塞,函数不能立即返回)。

可是使用Select就可以完成非阻塞(所谓非阻塞方式non-block,就是进程或线程执行此函数时不必非要等待事件的发生,一旦执行肯定返回,以返回值的不同来反映函数的执行情况,如果事件发生则与阻塞方式相同,若事件没有发生则返回一个代码来告知事件未发生,而进程或线程继续执行,所以效率较高)方式工作的程序,它能够监视我们需要监视的文件描述符的变化情况——读写或是异常。

下面详细介绍一下!Select的函数格式(我所说的是Unix系统下的伯克利socket编程,和windows下的有区别,一会儿说明):int select(int maxfdp,fd_set *readfds,fd_set *writefds,fd_set *errorfds,struct timeval *timeout);先说明两个结构体:第一,struct fd_set可以理解为一个集合,这个集合中存放的是文件描述符(filedescriptor),即文件句柄,这可以是我们所说的普通意义的文件,当然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* )。

linux的fd_set函数

linux的fd_set函数

linux的fd_set函数
fd_set是一个用于文件描述符操作的数据结构,在Linux系统
中通常用于多路复用IO操作,比如select、poll和epoll等函数。

它通常用于在一组文件描述符中进行操作,比如添加、删除、查询等。

fd_set结构通常是一个位向量,用于表示一组文件描述符。

在Linux中,fd_set结构通常是一个包含固定数量位的数组,每个位
对应一个文件描述符。

在进行文件描述符操作时,可以使用相关的
宏来设置、清除、查询文件描述符的状态。

在使用fd_set结构时,通常需要使用相关的宏来进行操作,比
如FD_ZERO、FD_SET、FD_CLR和FD_ISSET等。

FD_ZERO用于将
fd_set清零,FD_SET用于将指定的文件描述符加入到fd_set中,
FD_CLR用于将指定的文件描述符从fd_set中清除,FD_ISSET用于
查询指定的文件描述符是否在fd_set中。

在进行多路复用IO操作时,通常需要先初始化一个fd_set结构,然后将需要监控的文件描述符加入到fd_set中,然后传递给相
关的多路复用IO函数进行监控。

当有文件描述符就绪时,可以通过
查询fd_set结构来获取就绪的文件描述符。

总之,fd_set结构在Linux系统中是用于多路复用IO操作的一个重要数据结构,通过它可以方便地进行文件描述符的操作和查询,是实现IO多路复用的重要工具之一。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

select函数用于在非阻塞中,当一个套接字或一组套接字有信号时通知你,系统提供select函数来实现多路复用输入/输出模型,原型:#include sys/time.h>#include unistd.h>int select(int maxfd,fd_set *rdset,fd_set *wrset,fd_set *exset,struct timeval *timeout);参数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);用于测试指定的文件描述符是否在该集合中。

过去,一个fd_set通常只能包含<32的fd(文件描述字),因为fd_set其实只用了一个32位矢量来表示fd;现在,UNIX系统通常会在头文件<sys/select.h>中定义常量FD_SETSIZE,它是数据类型fd_set的描述字数量,其值通常是1024,这样就能表示<1024的fd。

根据fd_set的位矢量实现,我们可以重新理解操作fd_set的四个宏:fd_set set;FD_ZERO(&set);FD_SET(0, &set);FD_CLR(4, &set);FD_ISSET(5, &set); ―――――――――――――――――――――――――――――――――――――――注意fd的最大值必须<FD_SETSIZE。

―――――――――――――――――――――――――――――――――――――――select函数的接口比较简单:int select(int nfds, fd_set *readset, fd_set *writeset,fd_set* exceptset, struct tim *timeout);功能:测试指定的fd可读?可写?有异常条件待处理?参数:nfds需要检查的文件描述字个数(即检查到fd_set的第几位),数值应该比三组fd_set中所含的最大fd值更大,一般设为三组fd_set中所含的最大fd值加1(如在readset,writeset,exceptset中所含最大的fd为5,则nfds=6,因为fd是从0开始的)。

设这个值是为提高效率,使函数不必检查fd_set的所有1024位。

readset用来检查可读性的一组文件描述字。

writeset用来检查可写性的一组文件描述字。

exceptset用来检查是否有异常条件出现的文件描述字。

(注:错误不包括在异常条件之内)timeout用于描述一段时间长度,如果在这个时间内,需要监视的描述符没有事件发生则函数返回,返回值为0。

有三种可能:1.timeout=NULL(阻塞:select将一直被阻塞,直到某个文件描述符上发生了事件)2.timeout所指向的结构设为非零时间(等待固定时间:如果在指定的时间段里有事件发生或者时间耗尽,函数均返回)3.timeout所指向的结构,时间设为0(非阻塞:仅检测描述符集合的状态,然后立即返回,并不等待外部事件的发生)返回值:返回对应位仍然为1的fd的总数。

Remarks:三组fd_set均将某些fd位置0,只有那些可读,可写以及有异常条件待处理的fd位仍然为1。

举个例子,比如recv(), 在没有数据到来调用它的时候,你的线程将被阻塞,如果数据一直不来,你的线程就要阻塞很久.这样显然不好.所以采用select来查看套节字是否可读(也就是是否有数据读了)步骤如下——socket s;.....fd_set set;while(1){FD_ZERO(&set);//将你的套节字集合清空FD_SET(s, &set);//加入你感兴趣的套节字到集合,这里是一个读数据的套节字sselect(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,0011(4)执行select(6,&set,0,0,0)阻塞等待(5)若fd=1,fd=2上都发生可读事件,则select返回,此时set变为0000,0011。

注意:没有事件发生的fd=5被清空。

基于上面的讨论,可以轻松得出select模型的特点:(1)可监控的文件描述符个数取决与sizeof(fd_set)的值。

我这边服务器上sizeof(fd_set)=512,每bit表示一个文件描述符,则我服务器上支持的最大文件描述符是512*8=4096。

据说可调,另有说虽然可调,但调整上限受于编译内核时的变量值。

本人对调整fd_set的大小不太感兴趣,参考/CppExplore/archive/2008/03/21/45061.html中的模型2(1)可以有效突破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判断是否有时间发生)。

下面给一个伪码说明基本select模型的服务器模型:array[slect_len];nSock=0;array[nSock++]=listen_fd;(之前listen port已绑定并listen)maxfd=listen_fd;while{FD_ZERO(&set);foreach (fd in array){fd大于maxfd,则maxfd=fdFD_SET(fd,&set)}res=select(maxfd+1,&set,0,0,0);if(FD_ISSET(listen_fd,&set)){newfd=accept(listen_fd);array[nsock++]=newfd;if(--res=0) continue}foreach 下标1开始 (fd in array){if(FD_ISSET(fd,&set))执行读等相关操作如果错误或者关闭,则要删除该fd,将array中相应位置和最后一个元素互换就好,nsock减一if(--res=0) continue}}使用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);_sec = _usec = 0;rc = select(fd+1, &fds, NULL, NULL, &tv);if (rc < 0) //errorreturn -1;return FD_ISSET(fd,&fds) ? 1 : 0;}下面还有一个复杂一些的应用://这段代码将指定测试Socket的描述字的可读可写性,因为Socket使用的也是fduint32 SocketWait(TSocket *s,bool rd,bool wr,uint32 timems){fd_set rfds,wfds;#ifdef _WIN32TIM tv;#elsestruct tim tv;#endifFD_ZERO(&rfds);FD_ZERO(&wfds);if (rd) //TRUEFD_SET(*s,&rfds); //添加要测试的描述字if (wr) //FALSEFD_SET(*s,&wfds);_sec=timems/1000; //second_usec=timems%1000; //msfor (;;) //如果errno==EINTR,反复测试缓冲区的可读性switch(select((*s)+1,&rfds,&wfds,NULL,(timems==TIME_INFINITE?NULL:&tv))) //测试在规定的时间内套接口接收缓冲区中是否有数据可读{ //0--超时,-1--出错case 0:return 0;case (-1):if (SocketError()==EINTR)break;return 0; //有错但不是EINTRdefault:if (FD_ISSET(*s,&rfds)) //如果s是fds中的一员返回非0,否则返回0return 1;if (FD_ISSET(*s,&wfds))return 2;return 0;};}。

相关文档
最新文档