当前位置:首页 >> IT/计算机 >>

linux中select函数的使用


linux 中 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 可以理解为一个集合,这个集合中存放的是文件描述符 (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 值,表示不关心任何文件的写变化。 fd_set *errorfds 同上面两个参数的意图,用来监视文件错误异常。 struct timeval* timeout 是 select 的超时时间,这个参数至关重要,它可以 使 select 处于三种状态,第一,若将 NULL 以形参传入,即不传入时间结构,就 是将 select 置于阻塞状态,一定等到监视文件描述符集合中某个文件描述符发 生变化为止; 第二, 若将时间值设为 0 秒 0 毫秒, 就变成一个纯粹的非阻塞函数, 不管文件描述符是否有变化,都立刻返回继续执行,文件无变化返回 0,有变化 返回一个正值;第三,timeout 的值大于 0,这就是等待的超时时间,即 select 在 timeout 时间内阻塞,超时时间之内有事件到来就返回了,否则在超时后不管 怎样一定返回,返回值同上述。 返回值: 负值:select 错误 正值:某些文件可读写或出错 0:等待超时,没有可读写或 错误的文件 在有了 select 后可以写出像样的网络程序来!举个简单的例子,就是从网络上 接受数据写入一个文件中。 例子: 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 -1: 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 select()的机制中提供一 fd_set 的数据结构,实际上是一 long 类型的数组, 每一个数组元素都能与一打开的文件句柄(不管是 Socket 句柄,还是其他 文件或命名管道或设备句柄)建立联系,建立联系的工作由程序员完成, 当调用 select()时,由内核根据 IO 状态修改 fd_set 的内容,由此来通知执 行了 select()的进程哪一 Socket 或文件可读,下面具体解释: #include <sys/types.h> #include <sys/times.h> #include <sys/select.h> int select(nfds, readfds, writefds, exceptfds, timeout) int nfds; fd_set *readfds, *writefds, *exceptfds; struct timeval *timeout; ndfs:select 监视的文件句柄数,视进程中打开的文件数而定,一般设为呢要监 视各文件 中的最大文件号加一。 readfds:select 监视的可读文件句柄集合。 writefds: select 监视的可写文件句柄集合。 exceptfds:select 监视的异常文件句柄集合。 timeout:本次 select()的超时结束时间。(见/usr/sys/select.h, 可精确至百万分之一秒!) 当 readfds 或 writefds 中映象的文件可读或可写或超时,本次 select() 就结束返回。程序员利用一组系统提供的宏在 select()结束时便可判 断哪一文件可读或可写。对 Socket 编程特别有用的就是 readfds。 几只相关的宏解释如下: 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 表示可读写。 (关于 fd_set 及相关宏的定义见/usr/include/sys/types.h)

这样,你的 socket 只需在有东东读的时候才读入,大致如下: ... int sockfd; fd_set fdR; struct timeval timeout = ..; ... for(;;) { FD_ZERO(&fdR); FD_SET(sockfd, &fdR); switch (select(sockfd + 1, &fdR, NULL, &timeout)) { case -1: error handled by u; case 0: timeout hanled by u; default: if (FD_ISSET(sockfd)) { now u read or recv something; /* if sockfd is father and server socket, u can now accept() */ } } } 所以一个 FD_ISSET(sockfd)就相当通知了 sockfd 可读。 至于 struct timeval 在此的功能,请 man select。不同的 timeval 设置 使使 select()表现出超时结束、无超时阻塞和轮询三种特性。由于 timeval 可精确至百万分之一秒,所以 Windows 的 SetTimer()根本不算 什么。你可以用 select()做一个超级时钟。 FD_ACCEPT 的实现?依然如上,因为客户方 socket 请求连接时,会发送 连接请求报文,此时 select()当然会结束,FD_ISSET(sockfd)当然大 于零,因为有报文可读嘛!至于这方面的应用,主要在于服务方的父 Socket,你若不喜欢主动 accept(),可改为如上机制来 accept()。 至于 FD_CLOSE 的实现及处理,颇费了一堆 cpu 处理时间,未完待续。 -讨论关于利用 select()检测对方 Socket 关闭的问题: 仍然是本地 Socket 有东东可读,因为对方 Socket 关闭时,会发一个关闭连接 通知报文,会马上被 select()检测到的。关于 TCP 的连接(三次握手)和关

闭(二次握手)机制,敬请参考有关 TCP/IP 的书籍。 不知是什么原因,UNIX 好象没有提供通知进程关于 Socket 或 Pipe 对方关闭的 信号,也可能是 cpu 所知有限。总之,当对方关闭,一执行 recv()或 read(), 马上回返回-1,此时全局变量 errno 的值是 115,相应的 sys_errlist[errno] 为"Connect refused"(请参考/usr/include/sys/errno.h)。所以,在上 篇的 for(;;)...select()程序块中,当有东西可读时,一定要检查 recv()或 read()的返回值,返回-1 时要作出关断本地 Socket 的处理,否则 select()会 一直认为有东西读,其结果曾几令 cpu 伤心欲断针脚。不信你可以试试:不检 查 recv()返回结果,且将收到的东东(实际没收到)写至标准输出... 在有名管道的编程中也有类似问题出现。具体处理详见拙作:发布一个有用 的 Socket 客户方原码。 至于主动写 Socket 时对方突然关闭的处理则可以简单地捕捉信号 SIGPIPE 并作 出相应关断本地 Socket 等等的处理。 SIGPIPE 的解释是: 写入无读者方的管道。 在此不作赘述,请详 man signal。 以上是 cpu 在作 tcp/ip 数据传输实验积累的经验,若有错漏,请狂炮击之。 唉,昨天在 hacker 区被一帮孙子轰得差点儿没短路。ren cpu(奔腾的心) z80 补充关于 select 在异步(非阻塞)connect 中的应用,刚开始搞 socket 编程的时 候 我一直都用阻塞式的 connect,非阻塞 connect 的问题是由于当时搞 proxy scan 而提出的呵呵 通过在网上与网友们的交流及查找相关 FAQ,总算知道了怎么解决这一问题.同 样 用 select 可以很好地解决这一问题.大致过程是这样的: 1.将打开的 socket 设为非阻塞的,可以用 fcntl(socket, F_SETFL, O_NDELAY) 完 成(有的系统用 FNEDLAY 也可). 2.发 connect 调用,这时返回-1,但是 errno 被设为 EINPROGRESS,意即 connect 仍旧 在进行还没有完成. 3.将打开的 socket 设进被监视的可写(注意不是可读)文件集合用 select 进行监 视, 如果可写,用 getsockopt(socket, SOL_SOCKET, SO_ERROR, &error, sizeof(int)); 来得到 error 的值,如果为零,则 connect 成功.

在许多 unix 版本的 proxyscan 程序你都可以看到类似的过程,另外在 solaris 精华 区->编程技巧中有一个通用的带超时参数的 connect 模块.


相关文章:
Linux socket select 函数用法详解
Linux socket select 函数用法详解_计算机软件及应用_IT/计算机_专业资料。LINUX ...} //配置侦听 Socket //SO_REUSEADDR BOOL 允许套接口和一个已在使用中的地址...
linux中select函数
linux中select函数_IT/计算机_专业资料。select 函数: 系统提供 select 函数来实现多路复用输入/输出模型。原型: #include <sys/time.h> #include <unistd.h> se...
linux select 函数用法
linux select 函数用法表头文件 #include<sys/time.h> #include<sys/types.h> #include<unistd.h> 定义函数 int select(int n,fd_set * readfds,fd_set *...
linux网络编程select函数使用例程
linux网络编程select函数使用例程_计算机软件及应用_IT/计算机_专业资料。这个是我们课程编写的select()函数例程最近上课,老师非让我们用 select()函数编写一个网络程序...
linux中select函数的使用
linux下的Select函数 7页 1财富值如要投诉违规内容,请到百度文库投诉中心;如要提出功能问题或意见建议,请点击此处进行反馈。 linux中select函数的使用 linux中select...
linux 中select函数分析
linux中select函数的使用 7页 免费 select函数 7页 免费 Linux网络编程超级详细笔记... 43页 免费如要投诉违规内容,请到百度文库投诉中心;如要提出功能问题或意见...
Linux网络编程 - select函数的应用
Linux网络编程 - select函数的应用_计算机软件及应用_IT/计算机_专业资料。详细介绍了linux网络编程中select函数的应用。 select系统调用是用来让我们的程序监视多个文...
linux中select系统调用
linux中select系统调用_计算机软件及应用_IT/计算机_专业资料。select 系统调用是.../*然 后调用 select 函数*/ 这样就可以使用一个开关语句(switch 语句)来判断...
windows编程中select函数的使用
linux c 中 select(I/O 多工机制) 表头文件 #i nclude<sys/time.h> #i nclude<sys/types.h> #i nclude<unistd.h> 定义函数 int select(int n,fd_...
Oracle数据库Select函数基本用法
1、当使用聚集函数时,SELECT 中的列不出现在聚集函数中就必须出现在 GROUP BY 中,否则会出现语法错误;例如下列语句是非法的: SELECT department_id, COUNT(last_...
更多相关标签:
linux select函数 | linux select函数详解 | linux中select函数 | linux下select函数 | linux select使用 | select函数的使用 | linux crc32 函数使用 | linux select |