昌邑做网站的公司,重庆免费推广网站,wordpress加邮箱代码,什么网站资源多Select在Socket编程中还是比较重要的#xff0c;可是对于初学Socket的人来说都不太爱用Select写程序#xff0c;他们只是习惯写诸如 connect、accept、recv或recvfrom这样的阻塞程序#xff08;所谓阻塞方式block#xff0c;顾名思义#xff0c;就是进程或是线程执行到这…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* )。一会儿举例说明。 第二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都已经给定要写的文件已经打开 socksocket(...); bind(...); fpfopen(...); */ while(1) { FD_ZERO(fds); //每次循环都要清空集合否则不能检测描述符变化 FD_SET(sock,fds); //添加描述符 FD_SET(fp,fds); //同上 maxfdpsockfp?sock1:fp1; //描述符最大值加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 文章出处DIY部落(http://www.diybl.com/course/6_system/linux/Linuxjs/20090308/159832.html) linux c语言 select函数用法 表头文件 i ncludesys/time.h i ncludesys/types.h i ncludeunistd.h 定义函数 int select(int n,fd_set * readfds,fd_set * writefds,fd_set * exceptfds,struct timeval * timeout); 函数说明 select()用来等待文件描述词状态的改变。参数n代表最大的文件描述词加1参数readfds、writefds 和exceptfds 称为描述词组是用来回传该描述词的读写或例外的状况。底下的宏提供了处理这三种描述词组的方式: FD_CLR(inr fd,fd_set* set)用来清除描述词组set中相关fd 的位 FD_ISSET(int fd,fd_set *set)用来测试描述词组set中相关fd 的位是否为真 FD_SETint fd,fd_set*set用来设置描述词组set中相关fd的位 FD_ZEROfd_set *set 用来清除描述词组set的全部位 参数 timeout为结构timeval用来设置select()的等待时间其结构定义如下 struct timeval { time_t tv_sec; time_t tv_usec; }; 返回值 如果参数timeout设为NULL则表示select没有timeout。 错误代码 执行成功则返回文件描述词状态已改变的个数如果返回0代表在描述词状态改变前已超过timeout时间当有错误发生时则返回-1错误原因存于errno此时参数readfdswritefdsexceptfds和timeout的值变成不可预测。 EBADF 文件描述词为无效的或该文件已关闭 EINTR 此调用被信号所中断 EINVAL 参数n 为负值。 ENOMEM 核心内存不足 范例 常见的程序片段:fs_set readset FD_ZERO(readset); FD_SET(fd,readset); select(fd1,readset,NULL,NULL,NULL); if(FD_ISSET(fd,readset){……} 下面是linux环境下select的一个简单用法 i nclude sys/time.h i nclude stdio.h i nclude sys/types.h i nclude sys/stat.h i nclude fcntl.h i nclude assert.h int main () { int keyboard; int ret,i; char c; fd_set readfd; struct timeval timeout; keyboard open(/dev/tty,O_RDONLY | O_NONBLOCK); assert(keyboard0); while(1) { timeout.tv_sec1; timeout.tv_usec0; FD_ZERO(readfd); FD_SET(keyboard,readfd); retselect(keyboard1,readfd,NULL,NULL,timeout); if(FD_ISSET(keyboard,readfd)) { iread(keyboard,c,1); if(\nc) continue; printf(hehethe input is %c\n,c); if (qc) break; } } } 用来循环读取键盘输入 2007年9月17日将例子程序作一修改加上了time out,并且考虑了select得所有的情况 #include stdio.h #include sys/types.h #include sys/stat.h #include fcntl.h #include assert.h int main () { int keyboard; int ret,i; char c; fd_set readfd; struct timeval timeout; keyboard open(/dev/tty,O_RDONLY | O_NONBLOCK); assert(keyboard0); while(1) { timeout.tv_sec5; timeout.tv_usec0; FD_ZERO(readfd); FD_SET(keyboard,readfd); retselect(keyboard1,readfd,NULL,NULL,timeout); //select error when ret -1 if (ret -1) perror(select error); //data coming when ret0 else if (ret) { if(FD_ISSET(keyboard,readfd)) { iread(keyboard,c,1); if(\nc) continue; printf(hehethe input is %c\n,c); if (qc) break; } } //time out when ret 0 else if (ret 0) printf(time out\n); } } #include string.h #include unistd.h #include sys/time.h #include sys/types.h 下面是我写的一个例程 在标准输入读取9个字节数据。 用select函数实现超时判断 int main(int argc, char ** argv) { char buf[10] ; fd_set rdfds;// struct timeval tv; //store timeout int ret; // return val FD_ZERO(rdfds); //clear rdfds FD_SET(1, rdfds); //add stdin handle into rdfds tv.tv_sec 3; tv.tv_usec 500; ret select(1 1, rdfds, NULL, NULL, tv); if(ret 0) perror(\nselect); else if(ret 0) printf(\ntimeout); else { printf(\nret%d, ret); } if(FD_ISSET(1, rdfds)) { printf(\nreading); fread(buf, 9, 1, stdin); // read form stdin } // read(0, buf, 9); /* read from stdin */ // fprintf(stdout, %s\n, buf); /* write to stdout */ write(1, buf, strlen(buf)); //write to stdout printf(\n%d\n, strlen(buf)); return 0;