FD_SET,FD_ISSET的意思
- 格式:docx
- 大小:14.83 KB
- 文档页数:3
udp相关知识1.recvfrom函数解析#include <winsock.h>int PASCAL FAR recvfrom( SOCKET s, char FAR* buf, int len, int flags,struct sockaddr FAR* from, int FAR* fromlen);s:标识一个已连接套接口的描述字。
buf:接收数据缓冲区。
len:缓冲区长度。
flags:调用操作方式。
from:(可选)指针,指向装有源地址的缓冲区。
fromlen:(可选)指针,指向from缓冲区长度值例程:SOCKET sockSrv = socket(AF_INET,SOCK_DGRAM,0);char recvBuf[64];SOCKADDR_IN addrClient;int len = sizeof(SOCKADDR);BufSize = recvfrom(sockSrv , recvBuf,sizeof(recvBuf),0,(SOCKADDR*)&addrClient,&len);2.select 函数解析int select(int fds,fd_set* readfds,fd_set* writefds,fd_set* exceptfds,const struct timeval* timeout); fds:本参数忽略,仅起到兼容作用。
readfds:(可选)指针,指向一组等待可读性检查的套接口。
writefds:(可选)指针,指向一组等待可写性检查的套接口。
exceptfds:(可选)指针,指向一组等待错误检查的套接口。
timeout:select()最多等待时间,对阻塞操作则为NULL。
timeout为结构timeval,用来设置select()的等待时间,其结构定义如下struct timeval{time_t tv_sec; //second 秒time_t tv_usec; //microsecond 微妙};本函数用于确定一个或多个套接口的状态。
linux select区分读写Linux中的select函数是一个用于多路复用的系统调用,它可以同时监视多个文件描述符的状态,包括读、写和异常等事件。
在使用select函数时,我们可以通过设置相应的文件描述符集合来区分读和写。
首先,我们需要创建三个文件描述符集合:readfds、writefds和exceptfds。
readfds用于监视可读事件,writefds用于监视可写事件,exceptfds用于监视异常事件。
然后,我们需要将需要监视的文件描述符添加到相应的集合中。
对于需要监视读事件的文件描述符,我们可以使用FD_SET宏将其添加到readfds中;对于需要监视写事件的文件描述符,我们可以使用FD_SET宏将其添加到writefds中;对于需要监视异常事件的文件描述符,我们可以使用FD_SET宏将其添加到exceptfds中。
接下来,我们调用select函数,并传入最大文件描述符值加1、readfds、writefds和exceptfds作为参数。
select函数会阻塞程序执行,并等待指定的文件描述符集合中有事件发生。
当select函数返回时,我们可以通过检查相应的文件描述符集合来确定哪些文件描述符有可读或可写或异常事件发生。
对于每个文件描述符,在相应的集合中使用FD_ISSET宏进行检查即可。
如果返回值为真,则表示该文件描述符有相应类型的事件发生。
最后,我们可以根据检查结果进行相应处理。
例如,如果某个文件描述符在readfds中返回为真,则表示该文件描述符有可读事件发生,我们可以调用read函数来读取数据;如果某个文件描述符在writefds中返回为真,则表示该文件描述符有可写事件发生,我们可以调用write函数来写入数据;如果某个文件描述符在exceptfds中返回为真,则表示该文件描述符有异常事件发生,我们可以进行相应的处理。
总之,通过使用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 来获取。
fd_isset函数
fd_isset函数
fd_isset函数用于检查某一文件描述符是否设置在读写集合中,fd_isset函数的返回值为非零值(即TRUE)表示设置了某一文件描述符,否则(即为FALSE)表示未设置。
fd_isset函数原型规定如下:
int fd_isset( int fd, fd_set *set );
其中,fd为要检查的描述符,set是一个文件描述符集,可以是读、写、异常三种集合中的任意一种。
fd_isset函数返回值如下:
1.当描述符存在于指定的集合中时,返回真;
2.当描述符不存在于指定的集合中时,返回假;
3.当参数错误时,或者文件描述符超出了指定集合中的最大值,则返回一个负值。
该函数使用起来很简单,但是也有一些使用上的细节,如:
1.对于每一个描述符,调用fd_isset函数之前,必须先调用
fd_set函数;
2.另外,也要考虑fd_isset函数的回调函数一定是由fd_set 函数来调用的;
3.另外,在调用fd_isset函数之前,也要确保文件描述符至少有一个空闲块,否则,它可能会返回一个负值。
总之,fd_isset函数可以用来检查文件描述符是否设置了读、
写、异常三种集合中的任意一种,并且能够返回一个正确的值,从而保证程序的正确性和安全性。
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。
select用法c语言在C语言中,`select`是一个系统调用,用于在多个文件描述符上监听事件。
它主要用于I/O多路复用,可以同时监听多个文件描述符的可读、可写或异常等事件。
`select`函数的定义如下:```c#include <sys/select.h>int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);```参数解释:- `nfds`:需要监视文件描述符的总数,即设置最大的文件描述符号+1,可以简单理解为fd_set中描述符的个数。
- `readfds`:需要监视可读事件的文件描述符集合。
- `writefds`:需要监视可写事件的文件描述符集合。
- `exceptfds`:需要监视异常事件的文件描述符集合。
- `timeout`:等待的时间长度。
如果为NULL,则`select`函数将会一直阻塞,直到某个文件描述符准备好或者有信号中断;如果设置为0不阻塞,立即返回。
返回值:-如果超时时间到达前,有文件描述符就绪,返回就绪文件描述符的总数。
-如果超时时间到达前没有文件描述符就绪,则返回0。
-如果出错,返回-1,并设置errno。
示例代码如下:```c#include <stdio.h>#include <sys/select.h>int main() {fd_set rfds;struct timeval tv;int retval;//监听文件描述符0 (标准输入)FD_ZERO(&rfds);FD_SET(0, &rfds);//等待5秒_sec = 5;_usec = 0;//执行selectretval = select(1, &rfds, NULL, NULL, &tv); if (retval == -1) {perror("select");} else if (retval == 0) {printf("No data within five seconds.\n");} else {if (FD_ISSET(0, &rfds)) {printf("Data is available now.\n");}}return 0;}```上述代码中,我们将标准输入(文件描述符为0)添加到`readfds`参数中,然后等待5秒钟,如果在5秒钟内有输入数据,将会输出"Data is available now.",否则输出"No data within five seconds."。
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* )。
socket通信中select函数的使⽤和解释select函数的作⽤:select()在SOCKET编程中还是⽐较重要的,可是对于初学SOCKET的⼈来说都不太爱⽤select()写程序,他们只是习惯写诸如conncet()、accept()、recv()或recvfrom这样的阻塞程序(所谓阻塞⽅式block,顾名思义,就是进程或是线程执⾏到这些函数时必须等待某个事件发⽣,如果事件没有发⽣,进程或线程就被阻塞,函数不能⽴即返回)。
可是使⽤select()就可以完成⾮阻塞(所谓⾮阻塞⽅式non-block,就是进程或线程执⾏此函数时不必⾮要等待事件的发⽣,⼀旦执⾏肯定返回,以返回值的不同来反映函数的执⾏情况。
如果事件发⽣则与阻塞⽅式相同,若事件没有发⽣则返回⼀个代码来告知事件未发⽣,⽽进程或线程继续执⾏,所以效率⾼)⽅式⼯作的程序,它能够监视我们需要监视的⽂件描述符的变化情况——读写或是异常。
select函数格式:select()函数的格式(所说的是Unix系统下的Berkeley Socket编程,和Windows下的有区别,⼀会⼉说明):Unix系统下解释: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*)。
I/O多路复用分类:嵌入式 2011-05-03 16:12 163人阅读评论(0) 收藏举报一、五种I/O模型1、阻塞I/O模型最流行的I/O模型是阻塞I/O模型,缺省情形下,所有套接口都是阻塞的。
我们以数据报套接口为例来讲解此模型(我们使用UDP而不是TCP作为例子的原因在于就UDP而言,数据准备好读取的概念比较简单:要么整个数据报已经收到,要么还没有。
然而对于TCP来说,诸如套接口低潮标记等额外变量开始活动,导致这个概念变得复杂)。
进程调用recvfrom,其系统调用直到数据报到达且被拷贝到应用进程的缓冲区中或者发生错误才返回,期间一直在等待。
我们就说进程在从调用recvfrom开始到它返回的整段时间内是被阻塞的。
2、非阻塞I/O模型进程把一个套接口设置成非阻塞是在通知内核:当所请求的I/O操作非得把本进程投入睡眠才能完成时,不要把本进程投入睡眠,而是返回一个错误。
也就是说当数据没有到达时并不等待,而是以一个错误返回。
3、I/O复用模型调用select或poll,在这两个系统调用中的某一个上阻塞,而不是阻塞于真正I/O 系统调用。
阻塞于select调用,等待数据报套接口可读。
当select返回套接口可读条件时,调用recevfrom将数据报拷贝到应用缓冲区中。
4、信号驱动I/O模型首先开启套接口信号驱动I/O功能, 并通过系统调用sigaction安装一个信号处理函数(此系统调用立即返回,进程继续工作,它是非阻塞的)。
当数据报准备好被读时,就为该进程生成一个 SIGIO信号。
随即可以在信号处理程序中调用recvfrom来读数据报,井通知主循环数据已准备好被处理中。
也可以通知主循环,让它来读数据报。
5、异步I/O模型告知内核启动某个操作,并让内核在整个操作完成后(包括将数据从内核拷贝到用户自己的缓冲区)通知我们。
这种模型与信号驱动模型的主要区别是:信号驱动I/O:由内核通知我们何时可以启动一个I/O操作,异步I/O模型:由内核通知我们I/O操作何时完成。
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多路复用的重要工具之一。
fd_set参数过大
`fd_set`是用于文件描述符集合的宏,通常在处理I/O多路复用时使用,比如在`select()`或`poll()`函数中。
`fd_set`参数过大可能会导致一些问题。
首先,`fd_set`的第一个参数不能超过`FD_SETSIZE`。
在大多数系统上,
`FD_SETSIZE`被定义为1024,意味着集合中最多可以包含1024个文件描述符。
如果你尝试将超过这个数量的文件描述符添加到集合中,可能会导致未定义的行为。
其次,如果`fd_set`参数过大,可能会影响程序的性能。
因为每次调用
`select()`或`poll()`函数时,都需要遍历整个文件描述符集合,检查哪些文件描述符已经准备好进行读写操作。
如果集合中的文件描述符数量很大,那么遍历整个集合的时间就会增加,从而导致程序的性能下降。
因此,在设计使用`fd_set`的程序时,应该根据实际需要合理地选择集合的大小,避免参数过大导致的问题。
如果需要处理大量的文件描述符,可以考虑使用其他的方法,比如使用非阻塞I/O或者异步I/O等。
fd_set是文件句柄的集合。
FD_ZERO 清空这个集合;FD_SET往这个集合里面加入一个文件句柄;FD_ISSET 查看某一个文件句柄是否被设置了;其中,select的第二个参数是“可读”文件句柄的集合;第三个参数是“可写”文件句柄的集合;经常使用的就是这两个参数。
你可以看一下select函数的说明,里面有的。
'fd_set') 是一组文件描述符(fd)的集合。
由于fd_set类型的长度在不同平台上不同,因此应该用一组标准的宏定义来处理此类变量:fd_set set; FD_ZERO(&set); /* 将set清零 */ FD_SET(fd, &set); /* 将fd加入set*/ FD_CLR(fd, &set); /* 将fd从set中清除*/ FD_ISSET(fd, &set); /* 如果fd在set中则真*/在过去,一个fd_set通常只能包含少于等于32个文件描述符,因为fd_set其实只用了一个int的比特矢量来实现,在大多数情况下,检查fd_set能包括任意值的文件描述符是系统的责任,但确定你的fd_set到底能放多少有时你应该检查/修改宏FD_SETSIZE的值。
*这个值是系统相关的*,同时检查你的系统中的select() 的man手册。
有一些系统对多于1024个文件描述符的支持有问题。
#ifndef FD_SETSIZE#define FD_SETSIZE 64#endif /* FD_SETSIZE */typedef struct fd_set {u_int fd_count; /* how many are SET? */SOCKET fd_array[FD_SETSIZE]; /* an array of SOCKETs */} fd_set;extern int PASCAL FAR __WSAFDIsSet(SOCKET, fd_set FAR *);#define FD_CLR(fd, set) do { \u_int __i; \for (__i = 0; __i < ((fd_set FAR *)(set))->;fd_count ; __i++) { \if (((fd_set FAR *)(set))->;fd_array[__i] == fd) { \while (__i < ((fd_set FAR *)(set))->;fd_count-1) { \((fd_set FAR *)(set))->;fd_array[__i] = \((fd_set FAR *)(set))->;fd_array[__i+1]; \__i++; \} \((fd_set FAR *)(set))->;fd_count--; \break; \} \} \} while(0)#define FD_SET(fd, set) do { \u_int __i; \for (__i = 0; __i < ((fd_set FAR *)(set))->;fd_count; __i++) { \if (((fd_set FAR *)(set))->;fd_array[__i] == (fd)) { \break; \} \} \if (__i == ((fd_set FAR *)(set))->;fd_count) { \if (((fd_set FAR *)(set))->;fd_count < FD_SETSIZE) { \((fd_set FAR *)(set))->;fd_array[__i] = (fd); \((fd_set FAR *)(set))->;fd_count++; \} \} \} while(0)#define FD_ZERO(set) (((fd_set FAR *)(set))->;fd_count=0)#define FD_ISSET(fd, set) __WSAFDIsSet((SOCKET)(fd), (fd_set FAR *)(set)) typedef int32_t __fd_mask;#define _NFDBITS (sizeof(__fd_mask) * 8) /* 8 bits per byte */#define __howmany(x,y) (((x)+((y)-1))/(y))#ifndef _FD_SET# define _FD_SETtypedef struct __fd_set {long fds_bits[__howmany(FD_SETSIZE, (sizeof(long) * 8))];} fd_set;# ifndef _KERNEL# ifdef __cplusplusextern "C" {# endif /* __cplusplus */#ifdef _INCLUDE_HPUX_SOURCE# define FD_SET(n,p) (((__fd_mask *)((p)->;fds_bits))[(n)/_NFDBITS] |= (1 << ((n) % _NFDBITS)))# define FD_CLR(n,p) (((__fd_mask *)((p)->;fds_bits))[(n)/_NFDBITS] &= ~(1 << ((n) % _NFDBITS)))# define FD_ISSET(n,p) (((__fd_mask *)((p)->;fds_bits))[(n)/_NFDBITS] & (1 << ((n) % _NFDBITS)))# define FD_ZERO(p) memset((void *)(p), (int) 0, sizeof(*(p)))#else# define FD_SET(n,p) (__fd_set1(n, p))# define FD_CLR(n,p) (__fd_clr(n, p))# define FD_ISSET(n,p) (__fd_isset(n, p))# define FD_ZERO(p) memset((void *)(p), (int) 0, sizeof(fd_set))。
在《套接字socket及C/S通信的基本概念》和《WinSock编程基础》中,我们介绍了套接字的基本概念和WinSock API的基本调用规范。
我们讨论了阻塞模式/非阻塞模式和同步I/O和异步I/O等话题。
从概念的角度,阻塞模式因其简洁易用便于快速原型化,但在应付建立连接的多个套接字或在数据的收发量不均、时间不定时却极难管理。
另一方面,我们需要对非阻塞模式套接字的 WinSock API调用频繁返回的WSAEWOULDBLOCK错误加以判断处理也显得难于管理。
WinSock套接字I/O模型提供了管理I/O 完成通知的方法,帮助应用程序判断套接字何时可供读写。
共有6中类型的套接字I/O模型可让WinSock应用程序对I/O进行管理,它们包括blocking(阻塞)、select(选择)、WSAAsyncSelect(异步选择)、WSAEventSelect(事件选择)、overlapped(重叠)以及completionport(完成端口)。
本文讨论三种选择(都带select)模型。
1.基于套接字集合的select模型(1)select模型概述该模型时最初设计是在不使用UNIX操作系统的计算机上实现的,它们采用的是Berkeley套接字方案。
select模型已集成到Winsock 1.1中,它使那些想避免在套接字调用过程中被无辜“锁定”的应用程序,采取一种有序的方式,同时进行对多个套接字的管理。
之所以称其为“select模型”,是由于它的“中心思想”便是利用select函数,实现对I/O的管理!使用select模型,一般需要调用ioctlsocket 函数将一个套接字从锁定模式切换为非锁定模式。
// 将套接字s设置为非阻塞模式unsigned long nonBlocking = 1;ioctlsocket(s, FIONBIO, (u_long*)&nonBlocking);select模型本质上是一种分类处理思想,预先声明几个FD_SET(fd_set 结构)集合(使用FD_ZERO初始化),例如ReadSet,WriteSet,然后调用宏FD_SET(s,&ReadSet)将关注FD_READ事件的套接字s添加到ReadSet 集合,调用宏FD_SET(s,&WriteSet)将关注FD_WRITE事件的套接字s添加到WriteSet集合。
select函数及fd_set介绍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_ERROR2. fd_set结构体上⾯select函数中需要⽤到两个fd_set形参,这个结构体到底做什么⽤的呢fd_set其实这是⼀个数组的宏定义,实际上是⼀long类型的数组,每⼀个数组元素都能与⼀打开的⽂件句柄(socket、⽂件、管道、设备等)建⽴联系,建⽴联系的⼯作由程序员完成,当调⽤select()时,由内核根据IO状态修改fd_set的内容,由此来通知执⾏了select()的进程哪个句柄可读。
c select用法
C语言的select函数可以用于处理多路I/O复用,它是一种阻塞IO的实现方式,可以监听多个文件描述符上的IO事件,一旦有事件发生就会返回,从而避免了每个文件都阻塞等待,因此可以提高程序效率。
在这篇文章中,我们将为你详细介绍select的用法。
1.引入相关头文件
使用select需要引用<sys/time.h>和<sys/select.h>两个头文件,因为它是基于这两个头文件中的结构体进行实现的。
2.创建fd_set结构体
fd_set结构体是select函数的核心,它可以用于存储文件描述符。
其中需要注意的是,该结构体无法动态申请,因此在使用时需要手动添加和删除。
3.初始化fd_set结构体
在使用fd_set结构体之前,需要用FD_ZERO函数将其清零。
并使用FD_SET将文件描述符添加到集合中。
4.设置超时时间
select函数需要一个超时时间参数,表示程序多长时间等待一个事件发生。
如果设置为空,则会一直等待事件发生,直到有事件发生才会返回。
5.调用select函数
使用select函数可以实现多个文件描述符的监听,调用函数将直接被阻塞,一直等待IO时间发生。
6.处理已经发生的IO事件
当有事件发生后,select函数将返回,我们可以通过FD_ISSET 判断哪些文件描述符已经准备好。
并使用相应的函数进行处理。
以上是c的select函数的使用方法。
在实际开发中,我们需要对fd_set结构体和相关函数有足够的了解,以便更好地使用select 函数进行IO处理。
socketselect函数的详细讲解s原型int select(int ,fd_set* ,fd_set* ,fd_set* ,const struct timeval*);nfds:本参数忽略,仅起到兼容作⽤。
readfds:(可选)指针,指向⼀组等待可读性检查的套接⼝。
writefds:(可选)指针,指向⼀组等待可写性检查的套接⼝。
exceptfds:(可选)指针,指向⼀组等待错误检查的套接⼝。
timeout:select()最多等待时间,对阻塞操作则为NULL。
timeout为结构timeval,⽤来设置select()的等待时间,其结构定义如下struct timeval{time_t tv_sec; //second 秒time_t tv_usec; //microsecond 微妙};注释:本函数⽤于确定⼀个或多个套接⼝的状态。
对每⼀个套接⼝,调⽤者可查询它的可读性、可写性及错误状态信息。
⽤fd_set结构来表⽰⼀组等待检查的套接⼝。
在调⽤返回时,这个结构存有满⾜⼀定条件的套接⼝组的⼦集,并且select()返回满⾜条件的套接⼝的数⽬。
有⼀组宏可⽤于对fd_set的操作,这些宏与Berkeley Unix软件中的兼容,但内部的表达是完全不同的。
readfds参数标识等待可读性检查的套接⼝。
如果该套接⼝正处于监听listen()状态,则若有连接请求到达,该套接⼝便被标识为可读,这样⼀个accept()调⽤保证可以⽆阻塞完成。
对其他套接⼝⽽⾔,可读性意味着有排队数据供读取。
或者对于SOCK_STREAM类型套接⼝来说,相对于该套接⼝的虚套接⼝已关闭,于是recv()或recvfrom()操作均能⽆阻塞完成。
如果虚电路被“优雅地”中⽌,则recv()不读取数据⽴即返回;如果虚电路被强制复位,则recv()将以WSAECONNRESET错误⽴即返回。
如果SO_OOBINLINE选项被设置,则将检查带外数据是否存在(参见setsockopt())。
select用法c语言在C语言中,`select`是一个用于多路复用的系统调用,用于监视多个文件描述符的状态变化。
它是一种高效的I/O多路复用方法,可以同时监听多个文件描述符的读写状态,当有一个或多个文件描述符可读可写时,`select`函数就会返回。
它的原型如下:```cint select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);```其中各个参数的含义如下:- `nfds`:需要监听的最大文件描述符值加1,即待监听的文件描述符集合中最大的文件描述符值加1。
- `readfds`:要监听可读事件的文件描述符集合。
- `writefds`:要监听可写事件的文件描述符集合。
- `exceptfds`:要监听异常事件的文件描述符集合。
- `timeout`:设置超时时间,即`select`函数的阻塞时间,可以指定为`NULL`,表示永远阻塞直到有文件描述符就绪,也可以设置为一个具体的时间,当超过设定时间后,`select`函数会返回。
`select`函数的返回值表示就绪文件描述符的个数,如果返回0则表示超时,-1表示出错。
对于返回值大于0的情况,可以使用`FD_ISSET`宏来判断具体是哪个文件描述符可以进行读写操作。
拓展:在Linux系统中,`select`的缺点在于它的效率并不高,因为每次调用`select`函数都需要将待监听的文件描述符集合从用户态拷贝到内核态,并且在内核态中进行遍历。
在文件描述符数量很大时,这个过程会变得很慢。
另外,`select`监听的文件描述符集合是有限制的,一般默认为1024个。
为了解决这些问题,Linux提供了`epoll`等更高效的I/O多路复用方法。
C语⾔中select函数简介及使⽤select函数⽤来检查套接字描述符(sockets descriptors)是否已准备好读/写,提供了⼀种同时检查多个套接字的⽅法。
Linux中select函数的声明在/usr/include/x86_64-linux-gnu/sys/select.h⽂件中,Windows下select函数的声明在WinSock2.h ⽂件中,声明如下:// Linuxint select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);// Windowsint select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, const struct timeval *timeout);// macrosFD_SET(int fd, fd_set *set); // Add fd to the setFD_CLR(int fd, fd_set *set); // Remove fd from the setFD_ISSET(int fd, fd_set *set); // Return true if fd is in the setFD_ZERO(fd_set *set); // Clear all entries from the set不像socket中connect、accept、recv这⼏个函数属于阻塞⽅式,⽽select函数属于⾮阻塞⽅式。
在使⽤select函数时,会经常⽤到四个宏FD_SET(将⼀个指定的⽂件描述符加⼊集合)、FD_CLR(将⼀个指定的⽂件描述符从集合中删除)、FD_ISSET(检查⼀个指定的⽂件描述符是否在集合中)、FD_ZERO(清空集合)。
select()函数主要是建立在fd_set类型的基础上的。
fd_set(它比较重要所以先介绍一下)是一组文件描述字(fd)的集合,它用一位来表示一个fd(下面会仔细介绍),对于fd_set类型通过下面四个宏来操作:fd_set set;FD_ZERO(&set);FD_SET(fd, &set);FD_CLR(fd, &set);FD_ISSET(fd, &set);过去,一个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是文件句柄的集合。
FD_ZERO 清空这个集合;
FD_SET往这个集合里面加入一个文件句柄;
FD_ISSET 查看某一个文件句柄是否被设置了;
其中,select的第二个参数是“可读”文件句柄的集合;
第三个参数是“可写”文件句柄的集合;经常使用的就是这两个参数。
你可以看一下select函数的说明,里面有的。
'fd_set') 是一组文件描述符(fd)的集合。
由于fd_set类型的长度在不同平台上不同,因此应该用一组标准的宏定义来处理此类变量:
fd_set set; FD_ZERO(&set); /* 将set清零 */ FD_SET(fd, &set); /* 将fd加入set
*/ FD_CLR(fd, &set); /* 将fd从set中清除
*/ FD_ISSET(fd, &set); /* 如果fd在set中则真
*/
在过去,一个fd_set通常只能包含少于等于32个文件描述符,因为fd_set其实只用了一个int的比特矢量来实现,在大多数情况下,检查fd_set能包括任意值的文件描述符是系统的责任,但确定你的fd_set到底能放多少有时你应该检查/修改宏FD_SETSIZE的值。
*这个值是系统相关的*,同时检查你的系统中的select() 的man手册。
有一些系统对多于1024个文件描述符的支持有问题。
#ifndef FD_SETSIZE
#define FD_SETSIZE 64
#endif /* FD_SETSIZE */
typedef struct fd_set {
u_int fd_count; /* how many are SET? */
SOCKET fd_array[FD_SETSIZE]; /* an array of SOCKETs */
} fd_set;
extern int PASCAL FAR __WSAFDIsSet(SOCKET, fd_set FAR *);
#define FD_CLR(fd, set) do { \
u_int __i; \
for (__i = 0; __i < ((fd_set FAR *)(set))->;fd_count ; __i++) { \
if (((fd_set FAR *)(set))->;fd_array[__i] == fd) { \
while (__i < ((fd_set FAR *)(set))->;fd_count-1) { \
((fd_set FAR *)(set))->;fd_array[__i] = \
((fd_set FAR *)(set))->;fd_array[__i+1]; \
__i++; \
} \
((fd_set FAR *)(set))->;fd_count--; \
break; \
} \
} \
} while(0)
#define FD_SET(fd, set) do { \
u_int __i; \
for (__i = 0; __i < ((fd_set FAR *)(set))->;fd_count; __i++) { \
if (((fd_set FAR *)(set))->;fd_array[__i] == (fd)) { \
break; \
} \
} \
if (__i == ((fd_set FAR *)(set))->;fd_count) { \
if (((fd_set FAR *)(set))->;fd_count < FD_SETSIZE) { \
((fd_set FAR *)(set))->;fd_array[__i] = (fd); \
((fd_set FAR *)(set))->;fd_count++; \
} \
} \
} while(0)
#define FD_ZERO(set) (((fd_set FAR *)(set))->;fd_count=0)
#define FD_ISSET(fd, set) __WSAFDIsSet((SOCKET)(fd), (fd_set FAR *)(set)) typedef int32_t __fd_mask;
#define _NFDBITS (sizeof(__fd_mask) * 8) /* 8 bits per byte */
#define __howmany(x,y) (((x)+((y)-1))/(y))
#ifndef _FD_SET
# define _FD_SET
typedef struct __fd_set {
long fds_bits[__howmany(FD_SETSIZE, (sizeof(long) * 8))];
} fd_set;
# ifndef _KERNEL
# ifdef __cplusplus
extern "C" {
# endif /* __cplusplus */
#ifdef _INCLUDE_HPUX_SOURCE
# define FD_SET(n,p) (((__fd_mask *)((p)->;fds_bits))[(n)/_NFDBITS] |= (1 << ((n) % _NFDBITS)))
# define FD_CLR(n,p) (((__fd_mask *)((p)->;fds_bits))[(n)/_NFDBITS] &= ~(1 << ((n) % _NFDBITS)))
# define FD_ISSET(n,p) (((__fd_mask *)((p)->;fds_bits))[(n)/_NFDBITS] & (1 << ((n) % _NFDBITS)))
# define FD_ZERO(p) memset((void *)(p), (int) 0, sizeof(*(p)))
#else
# define FD_SET(n,p) (__fd_set1(n, p))
# define FD_CLR(n,p) (__fd_clr(n, p))
# define FD_ISSET(n,p) (__fd_isset(n, p))
# define FD_ZERO(p) memset((void *)(p), (int) 0, sizeof(fd_set))。