




已阅读5页,还剩5页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
Socket-Select2008-09-14 11:31Select在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* )。一会儿举例说明。第二,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秒轮询,要非阻塞就置0char buffer256=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=sockfp?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 机制, 因此当没有字符可读时, 程序处于阻塞状态,最小程度的占用CPU 资源, 在同一台机器上执行一个 server 和若干个client 时, 系统负载只有 0.1 左右, 而采用原来的非阻塞通信方法, 只运行一个 server, 系统负载就可以达到 1.5 左右. 因此我们推荐使用 selectsocket select()用法一、winsock中 #include 原型int select(int nfds,fd_set* readfds,fd_set* writefds,fd_set* exceptfds,const struct timeval* timeout);nfds:本参数忽略,仅起到兼容作用。 readfds:(可选)指针,指向一组等待可读性检查的套接口。 writefds:(可选)指针,指向一组等待可写性检查的套接口。 exceptfds:(可选)指针,指向一组等待错误检查的套接口。 timeout:select()最多等待时间,对阻塞操作则为NULL。注释: 本函数用于确定一个或多个套接口的状态。对每一个套接口,调用者可查询它的可读性、可写性及错误状态信息。用fd_set结构来表示一组等待检查的套接 口。在调用返回时,这个结构存有满足一定条件的套接口组的子集,并且select()返回满足条件的套接口的数目。有一组宏可用于对fd_set的操作, 这些宏与Berkeley Unix软件中的兼容,但内部的表达是完全不同的。 readfds参数标识等待可读性检查的套接口。如果该套接口正处于监听listen()状态,则若有连接请求到达,该套接口便被标识为可读,这样一个 accept()调用保证可以无阻塞完成。对其他套接口而言,可读性意味着有排队数据供读取。或者对于SOCK_STREAM类型套接口来说,相对于该套 接口的虚套接口已关闭,于是recv()或recvfrom()操作均能无阻塞完成。如果虚电路被“优雅地”中止,则recv()不读取数据立即返回;如 果虚电路被强制复位,则recv()将以WSAECONNRESET错误立即返回。如果SO_OOBINLINE选项被设置,则将检查带外数据是否存在 (参见setsockopt())。 writefds参数标识等待可写性检查的套接口。如果一个套接口正在connect()连接(非阻塞),可写性意味着连接顺利建立。如果套接口并未处于 connect()调用中,可写性意味着send()和sendto()调用将无阻塞完成。但并未指出这个保证在多长时间内有效,特别是在多线程环境 中。 exceptfds参数标识等待带外数据存在性或意味错误条件检查的套接口。请注意如果设置了SO_OOBINLINE选项为假FALSE,则只能用这种 方法来检查带外数据的存在与否。对于SO_STREAM类型套接口,远端造成的连接中止和KEEPALIVE错误都将被作为意味出错。如果套接口正在进行 连接connect()(非阻塞方式),则连接试图的失败将会表现在exceptfds参数中。 如果对readfds、writefds或exceptfds中任一个组类不感兴趣,可将它置为空NULL。 在winsock.h头文件中共定义了四个宏来操作描述字集。FD_SETSIZE变量用于确定一个集合中最多有多少描述字(FD_SETSIZE缺省值 为64,可在包含winsock.h前用#define FD_SETSIZE来改变该值)。对于内部表示,fd_set被表示成一个套接口的队列,最后一个有效元素的后续元素为INVAL_SOCKET。宏 为: FD_CLR(s,*set):从集合set中删除描述字s。 FD_ISSET(s,*set):若s为集合中一员,非零;否则为零。 FD_SET(s,*set):向集合添加描述字s。 FD_ZERO(*set):将set初始化为空集NULL。 timeout参数控制select()完成的时间。若timeout参数为空指针,则select()将一直阻塞到有一个描述字满足条件。否则的 话,timeout指向一个timeval结构,其中指定了select()调用在返回前等待多长时间。如果timeval为0,0,则 select()立即返回,这可用于探询所选套接口的状态。如果处于这种状态,则select()调用可认为是非阻塞的,且一切适用于非阻塞调用的假设都 适用于它。举例来说,阻塞钩子函数不应被调用,且WINDOWS套接口实现不应yield。返回值: select()调用返回处于就绪状态并且已经包含在fd_set结构中的描述字总数;如果超时则返回0;否则的话,返回SOCKET_ERROR错误,应用程序可通过WSAGetLastError()获取相应错误代码。错误代码: WSANOTINITIALISED:在使用此API之前应首先成功地调用WSAStartup()。 WSAENETDOWN:WINDOWS套接口实现检测到网络子系统失效。 WSAEINVAL:超时时间值非法。 WSAEINTR:通过一个WSACancelBlockingCall()来取消一个(阻塞的)调用。 WSAEINPROGRESS:一个阻塞的WINDOWS套接口调用正在运行中。 WSAENOTSOCK:描述字集合中包含有非套接口的元素。范例 : sock= socket(AF_INET,SOCK_STREAM,0); struct sockaddr_in addr; /告诉sock 应该再什么地方licencememset(&addr,0,sizeof(addr);addr.sin_family=AF_INET;addr.sin_port=htons(11111); /端口啦addr.sin_addr.s_addr=htonl(INADDR_ANY);/在本机的所有ip上开始监听bind (sock,(sockaddr *)&addr,sizeof(addr);/bind.listen(sock,5);/最大5个队列SOCKET socka; /这个用来接受一个连接fd_set rfd; / 描述符集 这个将用来测试有没有一个可用的连接struct timeval timeout;FD_ZERO(&rfd); /总是这样先清空一个描述符集timeout.tv_sec=60; /等下select用到这个timeout.tv_usec=0;u_long ul=1;ioctlsocket(sock,FIONBIO,&ul); /用非阻塞的连接/现在开始用selectFD_SET(sock,&rfd); /把sock放入要测试的描述符集 就是说把sock放入了rfd里面 这样下一步调用select对rfd进行测试的时候就会测试sock了(因为我们将sock放入的rdf) 一个描述符集可以包含多个被测试的描述符, if(select(sock+1,&rfd,0,0, &timeout)=0) /这个大括号接上面的,返回0那么就超过了timeout预定的时间 /处理.if(FD_ISSET(sock,&rfd) /有一个描述符准备好了socka=accept(sock,0,0); /一个用来测试读 一个用来测试写FD_ZERO(&rfd);FD_ZERO(&wfd);FD_SET(socka,&rfd);/把socka放入读描述符集FD_SET(sockb,&rfd);/把sockb放入读描述符集FD_SET(socka,&wfd);把socka放入写描述符集FD_SET(sockb,&wfd);把sockb放入写描述符集if(SOCKET_ERROR!=select(0,&rfd,&wfd,0,0) /测试这两个描述符集,永不超时 其中rfd只用来测试读 wfd只用来测试写 /没有错误if(FD_ISSET(socka,&rfd) /socka可读.if(FD_ISSET(sockb,&rfd) /sockb可读.if(FD_ISSET(socka,&wfd) /socka 可写.if(FD_ISSET(sockb,&wfd) /sockb可写.二、linux c中select(I/O多工机制)表头文件 i ncludei ncludei nclude定义函数 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_SET(int fd,fd_set*set);用来设置描述词组set中相关fd的位FD_ZERO(fd_set *set); 用来清除描述词组set的全部位参数 timeout为结构timeval,用来设置select()的等待时间,其结构定义如下struct timevaltime_t tv_sec;time_t tv_usec;返回值 如果参数timeout设为NULL则表示select()没有timeout。错误代码 执行成功则返回文件描述词状态已改变的个数,如果返回0代表在描述词状态改变前已超过timeout时间,当有错误发生时
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 土地使用合同范本及注意事项
- 外贸企业合同管理规范与风险防控
- 房地产交易流程标准与合同管理
- 2025年造价工程师案例分析模拟试卷:建筑工程造价编制与合同管理案例分析
- 城市停车收费管理服务合同
- 农业渔业资源共享协议合同书
- 包含维修和质保条款的协议
- 大型风电场设备维护合同范本
- 惠州市国有建设用地使用权转让合同5篇
- 小汽车出租合同5篇
- 2025年中国搬家公司行业市场运行动态及投资发展潜力分析报告
- 围手术期患者管理
- 光存储技术革新-洞察及研究
- 浙江科技大学《高等数学Ⅱ》2025-2026学年期末试卷(A卷)
- 13 唐诗五首《钱塘湖春行》课件
- (高清版)DB11∕T 2456-2025 消防安全管理人员能力评价规范
- 胎心监护及并发症处理
- 锁骨骨折术后护理
- 眼科常见疾病及其用药
- 脑疝患者的急救及护理
- 2024年全市首届档案职业技能竞赛考试题库(含答案)
评论
0/150
提交评论