select()函数以及FD_ZERO、FD_SET、FD_CLR、FD_ISSET
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函数是一种多路复用函数,它的作用是在多个文件描述符上等待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 是一个重要的系统调用,用于同时监视多个文件描述符的可读、可写和出错条件。
它是实现多路复用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函数被广泛用于处理多个输入/输出(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)[指南]
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函数分为阻塞和非阻塞两种方式,比如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在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函数
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多路复用的重要工具之一。
Linux中select函数学习及实例笔记
Linux中select函数学习及实例笔记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其中,现在比较流行的I/O模型是阻塞I/O模型.阻塞I/O是当应用程序和内核交换数据时,由于内核还没有准备好数据,那么应用程序必须进行阻塞,不能继续执行,直到内核的数据准备好!应用程序取到数据返回后,阻塞过程结束!但返回的结果也并不一定是正确的!这里只是举一个简单的例子!也许情况会更加的复杂!非阻塞I/O,例如在和内核交换数据时,如果内核的数据没有准备好,那么应用程序不会一真等待,会有一个返回信息,以判断是那里出了问题!这样有助于确认是在那个阶段出了问题!I/O复用,我们就可以调用系统调用select和poll!在这两个系统调用中的某一个阻塞,而不是真正的阻塞I/O系统调用!下面主要介绍I/O复用中的select函数!select函数可以指示内核等待多个事件中的任一个发生,仅在一个或多个事件发生,或者等待一个足够的时间后才唤醒进程!select函数的原型如下:#include <sys/types.h>#include<sys/time.h>int select (int maxfdp1,fd_set *readset,fd_set * writeset,fd_set excpetset,const struct timeval *timeout);返回值:准备好的描述符的正数目 0---超时 -1---出错!其中最后一个参数是一个结构体的指针,它表示等待内核中的一组描述符任一个准备好需要花费多久的时间!其中timeval指定了秒数和微秒数。
struct timeval{long tv_sec;//秒数long tv_usec;//微秒数};将 timeout设置为空指针时,会永远等待下去,等待固定的时间:如果timeout指向的timeval中的具体的值时,会等待一个固定的时间,不等待立刻返回,这时timeval中的tv_sec和tv_usec为0.select有三个可能的返回值。
select函数的作用
select函数的作用在网络编程中,当一个服务器需要同时处理多个客户端的连接请求时,就需要使用到select函数。
select函数是一种I/O多路复用机制,它可以同时监视多个文件描述符的可读、可写和异常等事件,当任意一个文件描述符就绪时,select函数将通知服务器进行相应的处理。
通过使用select函数,服务器可以实现并发处理多个客户端请求,提高系统的效率。
函数定义#include <sys/select.h>int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, str uct timeval *timeout);•nfds:要检查的文件描述符数量,即最大文件描述符值加1。
•readfds:指向fd_set类型的指针,用于检查可读事件。
•writefds:指向fd_set类型的指针,用于检查可写事件。
•exceptfds:指向fd_set类型的指针,用于检查异常事件。
•timeout:设置超时时间,如果为NULL,则表示永远等待。
函数用途select函数主要用于以下几个方面:1.监视文件描述符状态:通过将需要监视的文件描述符添加到对应的fd_set集合中,并传递给select函数,在超时时间内等待这些文件描述符中任意一个就绪(可读、可写或异常)。
2.处理就绪事件:当某个文件描述符就绪时,select函数将返回,程序可以根据返回的结果进行相应的处理。
例如,可读事件表示有数据可以读取,可写事件表示数据可以写入,异常事件表示发生了异常情况。
3.非阻塞等待:通过设置timeout为0,select函数将立即返回。
这样可以实现非阻塞等待,程序可以继续执行其他任务。
函数工作方式select函数的工作方式如下:1.程序通过将需要监视的文件描述符添加到对应的fd_set集合中,并设置超时时间。
2.调用select函数,并传递上述参数。
c++select函数的使用案例
c++select函数的使用案例c++中的select函数可以实现多路复用的网络通信,用于监听多个文件描述符,一旦其中任意一个文件描述符发生了连接就可以对其进行处理。
下面是一个简单的使用案例:```#include <iostream>#include <sys/types.h>#include <sys/socket.h>#include <arpa/inet.h>#include <unistd.h>#include <fcntl.h>#include <sys/select.h>#include <cstring>using namespace std;int main() {int sockfd = socket(AF_INET, SOCK_STREAM, 0);if (sockfd < 0) {cerr << "Failed to create socket." << endl;return -1;}int flags = fcntl(sockfd, F_GETFL, 0);fcntl(sockfd, F_SETFL, flags | O_NONBLOCK); //设置非阻塞 struct sockaddr_in local;local.sin_family = AF_INET;local.sin_addr.s_addr = htonl(INADDR_ANY);local.sin_port = htons(8181);bind(sockfd, (struct sockaddr *)&local,sizeof(local));listen(sockfd, 100); //监听客户端连接int maxfd = sockfd;fd_set readfds, allfds;FD_ZERO(&allfds);FD_SET(sockfd, &allfds); //添加sockfd到监视列表 while (true) {readfds = allfds; //要监视的文件描述符列表select(maxfd + 1, &readfds, NULL, NULL, NULL);//有客户端连接时处理连接请求if (FD_ISSET(sockfd, &readfds)) {struct sockaddr_in client; //客户端地址socklen_t len = sizeof(client);int clientfd = accept(sockfd, (struct sockaddr *)&client, &len);if (clientfd >= 0) {cout << "New client connected from " <<inet_ntoa(client.sin_addr) << endl;FD_SET(clientfd, &allfds); //将客户端sockfd添加到监视列表if (clientfd > maxfd) maxfd = clientfd;}}//处理客户端数据请求for (int i = sockfd + 1; i <= maxfd; i++) {if (FD_ISSET(i, &readfds)) {char buf[1024] = {0};int n = recv(i, buf, sizeof(buf), 0);if (n > 0) {cout << "Received data from client " << i << ": " << buf << endl;} else {//客户端断开连接cout << "Client " << i << " disconnected." << endl;close(i);FD_CLR(i, &allfds); //将客户端sockfd从监视列表中删除}}}}close(sockfd);return 0;}```在上述代码中,使用了FD_ZERO、FD_SET、FD_ISSET、FD_CLR等select函数的相关宏定义。
select函数及fd_set介绍
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用法
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处理。
select函数详解及实例分析
select函数详解及实例分析Select函数在Socket编程中还是比较重要的,可是对于初学Socket的人来说都不太爱用Select写程序,他们只是习惯写诸如connect、accept、recv或recvfrom这样的阻塞程序(所谓阻塞方式block,顾名思义,就是进程或是线程执行到这些函数时必须等待某个事件的发生,如果事件没有发生,进程或线程就被阻塞,函数不能立即返回)。
可是使用Select就可以完成非阻塞(所谓非阻塞方式non-block,就是进程或线程执行此函数时不必非要等待事件的发生,一旦执行肯定返回,以返回值的不同来反映函数的执行情况,如果事件发生则与阻塞方式相同,若事件没有发生则返回一个代码来告知事件未发生,而进程或线程继续执行,所以效率较高)方式工作的程序,它能够监视我们需要监视的文件描述符的变化情况——读写或是异常。
下面详细介绍一下!先说明两个结构体:1、select机制中提供了一个数据结构 struct fd_set ,可以理解为一个集合,实际上是一个位图,每一个特定为来标志相应大小文件描述符,这个集合中存放的是文件描述符(file descriptor),即文件句柄(也就是位图上的每一位都能与一个打开的文件句柄(文件描述符)建立联系,这个工作由程序员来完成),这可以是我们所说的普通意义的文件,当然Unix下任何设备、管道、FIFO等都是文件形式,全部包括在内,所以毫无疑问一个socket就是一个文件,socket句柄就是一个文件描述符。
fd_set集合可以通过一些宏由人为来操作,程序员通过操作4类宏,来完成最fd_set的操作:(1)、FD_ZERO(fd_set *) 清空一个文件描述符集合;(2)、FD_SET(int ,fd_set *)将一个文件描述符添加到一个指定的文件描述符集合中;(3)、FD_CLR(int ,fd_set*) 将一个给定的文件描述符从集合中删除;(4)、FD_ISSET(int ,fd_set* )检查集合中指定的文件描述符是否可以读写。
VxWorks编程常用函数说明
write(fd, &data, sizeof(data));
… …
close(fd);
}
memDevDelete("/mem/mem1");
}
3、通过Select函数实现多个IO监听:selectLib.h
பைடு நூலகம்printf("Message send failed!");
}
}
taskReceive() {
uchar_t ch;
msgQReceive(msgQID, &ch, 1, WAIT_FOREVER); /* 这里任务会阻塞 */
printf("Received from msgq: %c ", ch);
FD_ZERO(&fdset) 清除所有监听位
FD_ISSET(fd, &fdset) fd是否有数据
例子,其中MAX意为取最大值:
Init() {
struct fd_set readFds;
int fds[4];
int width;
fds[0] = open(..);… …;fds[3] = open(..); /* 打开IO */
… …; /* 进行读写操作 */
}
}
/* } */
}
四、多任务环境的编程:
1、任务控制:taskLib.h
taskSpawn( ) - 创建任务
taskInit( ) -初始化任务,用户自己指定栈和PCB地址
linux中select的返回值 -回复
linux中select的返回值-回复Linux中的select函数是用于监视多个文件描述符的状态变化的一个系统调用。
它可以同时监视多个文件描述符,当这些文件描述符中的一个或多个发生可读、可写或异常等事件时,select函数会返回,并将发生变化的文件描述符集合返回给用户程序。
在本文中,我们将详细介绍select函数的返回值及其含义,以及如何使用select函数来实现一个简单的并发服务器。
一、select函数的返回值在Linux中,select函数的原型如下:c#include <sys/select.h>int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set*exceptfds, struct timeval *timeout);其中各个参数的含义如下:- nfds:监视的文件描述符的数量,即待监视的文件描述符集合中的最大文件描述符加1。
- readfds:可读事件的文件描述符集合。
- writefds:可写事件的文件描述符集合。
- exceptfds:异常事件的文件描述符集合。
- timeout:超时参数,指定select函数的超时时间。
select函数的返回值为大于0的整数,表示发生变化的文件描述符的数量。
如果返回0,则表示在超时时间内没有发生任何事件。
如果返回-1,则表示select函数调用出现了错误,可以通过errno变量获取具体的错误码。
二、select函数返回的文件描述符集合在select函数中,可以通过一系列宏来操作文件描述符集合。
在头文件<sys/select.h>中定义了以下几个宏:- FD_ZERO(fd_set *set):将文件描述符集合清空。
- FD_SET(int fd, fd_set *set):将文件描述符fd添加到文件描述符集合set中。
- FD_CLR(int fd, fd_set *set):将文件描述符fd从文件描述符集合set中移除。
用select侦测TCP连接是否被对方关闭
#include
#include
#include
int select(nfds, readfds, writefds, exceptfds, timeout)
int nfds;
fd_set *readfds, *writefds, *exceptfds;
struct timeval *timeout;
为"Connect refused"(请参考/usr/include/sys/errno.h)。所以,在上
篇的for(;;)...select()程序块中,当有东西可读时,一定要检查recv()或
read()的返回值,返回-1时要作出关断本地Socket的处理,否则select()会
一直认为有东西读,其结果曾几令cpu伤心欲断针脚。不信你可以试试:不检
CAsyncSocket,都提供了诸如 FD_ACCEPT、FD_READ、FD_CLOSE 之类的消息 供编程人员捕捉并处理。FD_ACCEPT 通知进程有客户方Socket请求连接, FD_READ通知进程本地Socket有东东可读,FD_CLOSE通知进程对方Socket已 关闭。那么,BSD Socket 是不是真的相形见拙呢?
select()的机制中提供一fd_set的数据结构,实际上是一long类型的数组, 每一个数组元素都能与一打开的文件句柄(不管是Socket句柄,还是其他 文件或命名管道或设备句柄)建立联系,建立联系的工作由程序员完成, 当调用select()时,由内核根据IO状态修改fd_set的内容,由此来通知执 行了select()的进程哪一Socket或文件可读,下面具体解释:
成(有的系统用FNEDLAY也可).
select()函数以及FD_ZERO、FD_SET、FD_CLR、FD_ISSET(转)
select()函数以及FD_ZERO、FD_SET、FD_CLR、FD_ISSET(转)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)⽤于测试指定的⽂件描述符是否在该集合中。
fd_set
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开始的)。
lwip select用法
lwip select用法
lwip是一个开源的TCP/IP协议栈。
而select则是其中一个用于
I/O多路复用的API。
使用select可以让我们同时监视多个socket的
状态,以便于读写数据。
lwip的select函数比较简单,其函数签名如下:
```
int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);
```
其中参数意义如下:
- nfds:要检查的最大文件描述符数目,一般是要检查的最大文
件描述符+1。
- readfds、writefds和exceptfds:fd_set类型,指向需要监视的可读、可写和异常事件的文件描述符集合。
这些fd_set类型的参数应该
使用FD_ZERO、FD_CLR和FD_SET来初始化或者修改。
- timeout:超时时间,以微秒为单位。
如果为0,select将立即返回。
如果为NULL,将等待直到有事件发生。
select函数的返回值是三个文件描述符集合中需要操作的文件描述符的总数(即符合条件的文件描述符的总数)。
如果没有事件发生,select将返回0;如果出错,返回-1。
因此,可以在需要接受多个socket数据的程序中,使用select
函数来实现I/O多路复用,以提高程序效率。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 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;};}。