Linux下I/O多路转接之select --fd_set

http://blog.csdn.net/li_ning_/article/details/52165993

fd_set

你终于还是来了,能看到这个标题进来的,我想,你一定是和我遇到了一样的问题,一样的疑惑,接下来几个小时,我一定竭尽全力,写出我想说的,希望也正是你所需要的:

关于Linux下I/O多路转接之select,我不想太多的解释,用较少的文章引出今天我要说的问题:fd_set...自我感觉,这个东西,是理解select的关键。

一、关于select函数:


以上只是截屏,以保证本人说的是真话,下面解释:

        系统提供select函数来实现多路复用输入/输出模型。select系统调用是用来让我们的程序监视多个文件句柄的状态变化的。

程序会停在select这里等待,直到被监视的文件句柄有一个或 多个发生了状态改变。关于文件句柄,其实就是一个整数,我们最熟悉的句柄是0、1、2三 个,

0是标准输入,1是标准输出,2是标准错误输出。0、1、2是整数表示的,对应的FILE * 结构的表示就是stdin、stdout、stderr。 

1.参数nfds是需要监视的最大的文件描述符值+1; 

2.rdset,wrset,exset分别对应于需要检测的可读文件描述符的集合,可写文件描述符的集合及异常文件描述符的集合。 

3.struct timeval结构用于描述一段时间长度,如果在这个时间内,需要监视的描述符没有事件发生则函数返回,返回值为0。 

下面的宏提供了处理这三种描述词组的方式:

1. FD_CLR(inr fd,fd_set* set);用来清除描述词组set中相关fd 的位。

2. FD_ISSET(int fd,fd_set *set);用来测试描述词组set中相关fd 的位是否为真 。

3.FD_SET(int fd,fd_set*set);用来设置描述词组set中相关fd的位 。

4.FD_ZERO(fd_set *set);用来清除描述词组set的全部位 参数timeout为结构timeval,用来设置select()的等待时间,其结构定义如下:

结构体成员两个,第一个单位是秒,第二个单位是微妙 ,作用是时间为两个之和; 

更多关于select的应用,咱移驾看这位大神:http://blog.sina.com.cn/s/blog_5c8d13830100pwaf.html      关于select的使用,理解了也就好弄了,关于应用,后附代码:

下面才是我想说的东西:

二、fd_set:

1>>fd_set是什么:

         select()机制中提供一fd_set的数据结构,可以理解为一个集合,实际上是一个位图,每一个特定位来标志相应大小文件描述符,这个集合中存放的是文件描述符,即就是文件句柄(不管是socket句柄,还是其他文件或命名管道或设备句柄)建立联系,建立联系的工作由程序员完成,当调用select()时,由内核根据IO状态修改fd_set的内容,由此来通知执行了select()的进程哪一socket或文件可读。Unix下任何设备、管道、FIFO等都是文件形式,全部包括在内,所以毫无疑问一个socket就是一个文件,socket句柄就是一个文件描述符。fd_set集合可以通过一些宏由人为来操作,程序员通过操作4类宏,来完成最fd_set的操作,在上文已经提及。

2>>fe_set怎么表示:


其中readfds、writefds等都是fd_set类型,其中的每一位都表示一个fd,即文件描述符。

3>>fd_set用法:

过去,一个fd_set通常只能包含<32的fd(文件描述字),因为fd_set其实只用了一个32位矢量来表示fd;现在,UNIX系统通常会在头文件中定义常量FD_SETSIZE,它是数据类型fd_set的描述字数量,其值通常是1024,这样就能表示<1024的fd。根据fd_set的位矢量实现,我们可以重新理解操作fd_set的四个宏: 

 fd_set set;

FD_ZERO(&set);      /*将set的所有位置0,如set在内存中占8位则将set置为00000000*/

FD_SET(0, &set);    /* 将set的第0位置1,如set原来是00000000,则现在变为10000000,这样fd==1的文件描述字就被加进set中了 */

FD_CLR(4, &set);    /*将set的第4位置0,如set原来是10001000,则现在变为10000000,这样fd==4的文件描述字就被从set中清除了 */ 

FD_ISSET(5, &set);  /* 测试set的第5位是否为1,如果set原来是10000100,则返回非零,表明fd==5的文件描述字在set中;否则返回0*/

我们在回到原函数:select

 int select(int nfds, fd_set *readset, fd_set *writeset,fd_set* exceptset, struct timeval *timeout); 

 功能:测试指定的fd可读、可写、有异常条件待处理。     

参数:

1.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:有三种可能:

1.  timeout=NULL(阻塞:直到有一个fd位被置为1函数才返回)

2.  timeout所指向的结构设为非零时间(等待固定时间:有一个fd位被置为1或者时间耗尽,函数均返回)

3.  timeout所指向的结构,时间设为0(非阻塞:函数检查完每个fd后立即返回) 

返回值: 

     

1.当监视的相应的文件描述符集中满足条件时,比如说读文件描述符集中有数据到来时,内核(I/O)根据状态修改文件描述符集,并返回一个大于0的数。

2.当没有满足条件的文件描述符,且设置的timeval监控时间超时时,select函数会返回一个为0的值。

3.当select返回负值时,发生错误。

备注:

三组fd_set均将某些fd位置0,只有那些可读,可写以及有异常条件待处理的fd位仍然为1。

使用select函数的过程一般是:

先调用宏FD_ZERO将指定的fd_set清零,然后调用宏FD_SET将需要测试的fd加入fd_set,接着调用函数select测试fd_set中的所有fd,最后用宏FD_ISSET检查某个fd在函数select调用后,相应位是否仍然为1。

以下是一个测试单个文件描述字可读性的例子:

     

基于select实现的网络服务器和客户端:

server.c

[cpp] view plain copy
  1. #include<stdio.h>    
  2. #include<sys/types.h>    
  3. #include<sys/socket.h>    
  4. #include<unistd.h>    
  5. #include<stdlib.h>    
  6. #include<errno.h>    
  7. #include<arpa/inet.h>    
  8. #include<netinet/in.h>    
  9. #include<string.h>    
  10. #include<signal.h>    
  11. #include<sys/wait.h>    
  12.   
  13. /* 
  14.  *网络服务器,select参与调度 
  15.  * */  
  16.   
  17. //ERR_EXIT(M)是一个错误退出宏  
  18. #define ERR_EXIT(m) \    
  19.     do { \    
  20.         perror(m); \    
  21.         exit(EXIT_FAILURE); \    
  22.     } while (0)    
  23.     
  24.     
  25. int main(void)    
  26. {      
  27.     signal(SIGPIPE, SIG_IGN);  
  28.   
  29.     //1.创建套接字  
  30.     int listenfd;                 //被动套接字(文件描述符),即只可以accept, 监听套接字    
  31.     if ((listenfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)      
  32.         ERR_EXIT("socket error"); //调用上边的宏   
  33.     
  34.     struct sockaddr_in servaddr;  
  35.   
  36.     //memset(&servaddr, 0, sizeof(servaddr));    
  37.     //三个结构体成员  
  38.     //设置本地IP 和端口  
  39.     servaddr.sin_family = AF_INET;    
  40.     servaddr.sin_port = htons(8080);    
  41.     servaddr.sin_addr.s_addr = htonl(INADDR_ANY);     
  42.     /* servaddr.sin_addr.s_addr = inet_addr("127.0.0.1"); */    
  43.     /* inet_aton("127.0.0.1", &servaddr.sin_addr); */    
  44.         
  45.     //2.设置套接字属性  
  46.     int on = 1;    
  47.     if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)    
  48.         ERR_EXIT("setsockopt error");    
  49.       
  50.     //3.绑定  
  51.     if (bind(listenfd, (struct sockaddr*)&servaddr,sizeof(servaddr)) < 0)    
  52.         ERR_EXIT("bind error");    
  53.     
  54.     //4.监听  
  55.     if (listen(listenfd, SOMAXCONN) < 0) //listen应在socket和bind之后,而在accept之前    
  56.         ERR_EXIT("listen error");    
  57.         
  58.     struct sockaddr_in peeraddr; //传出参数    
  59.     socklen_t peerlen = sizeof(peeraddr); //传入传出参数,必须有初始值    
  60.         
  61.     int conn; // 已连接套接字(变为主动套接字,即可以主动connect)    
  62.     int i;    
  63.     int client[FD_SETSIZE];    
  64.     int maxi = 0; // client数组中最大不空闲位置的下标    
  65.     for (i = 0; i < FD_SETSIZE; i++)    
  66.         client[i] = -1;    
  67.     
  68.     int nready;    
  69.     int maxfd = listenfd;    
  70.     fd_set rset;    
  71.     fd_set allset;    
  72.     FD_ZERO(&rset);    
  73.     FD_ZERO(&allset);    
  74.     FD_SET(listenfd, &allset);    
  75.     
  76.     while (1) {    
  77.         rset = allset;    
  78.         nready = select(maxfd + 1, &rset, NULL, NULL, NULL);    
  79.         if (nready == -1) {    
  80.             if (errno == EINTR)    
  81.                 continue;    
  82.             ERR_EXIT("select error");    
  83.         }    
  84.     
  85.         if (nready == 0)    
  86.             continue;    
  87.     
  88.         if (FD_ISSET(listenfd, &rset)) {    
  89.             
  90.             conn = accept(listenfd, (struct sockaddr*)&peeraddr, &peerlen);  //accept不再阻塞    
  91.             if (conn == -1)    
  92.                 ERR_EXIT("accept error");    
  93.                 
  94.             for (i = 0; i < FD_SETSIZE; i++) {    
  95.                 if (client[i] < 0) {    
  96.                     client[i] = conn;    
  97.                     if (i > maxi)    
  98.                         maxi = i;    
  99.                     break;    
  100.                 }     
  101.             }    
  102.                 
  103.             if (i == FD_SETSIZE) {    
  104.                 fprintf(stderr, "too many clients\n");    
  105.                 exit(EXIT_FAILURE);    
  106.             }    
  107.     
  108.             printf("recv connect ip=%s port=%d\n", inet_ntoa(peeraddr.sin_addr),    
  109.                 ntohs(peeraddr.sin_port));    
  110.     
  111.             FD_SET(conn, &allset);    
  112.             if (conn > maxfd)    
  113.                 maxfd = conn;    
  114.     
  115.             if (--nready <= 0)    
  116.                 continue;    
  117.         }    
  118.     
  119.         for (i = 0; i <= maxi; i++) {    
  120.             conn = client[i];    
  121.             if (conn == -1)    
  122.                 continue;    
  123.     
  124.             if (FD_ISSET(conn, &rset)) {    
  125.                     
  126.                 char recvbuf[1024] = {0};    
  127.                 int ret = read(conn, recvbuf, 1024);    
  128.                 if (ret == -1)    
  129.                     ERR_EXIT("readline error");    
  130.                 else if (ret  == 0) { //客户端关闭     
  131.                     printf("client close \n");    
  132.                     FD_CLR(conn, &allset);    
  133.                     client[i] = -1;    
  134.                     close(conn);    
  135.                 }    
  136.             
  137.                 fputs(recvbuf, stdout);    
  138.                 write(conn, recvbuf, strlen(recvbuf));    
  139.                     
  140.                 if (--nready <= 0)    
  141.                     break;     
  142.             }    
  143.         }    
  144.     }           
  145.     return 0;    
  146. }   
client.c

[cpp] view plain copy
  1. #include<stdio.h>    
  2. #include<sys/types.h>    
  3. #include<sys/socket.h>    
  4. #include<unistd.h>    
  5. #include<stdlib.h>    
  6. #include<errno.h>    
  7. #include<arpa/inet.h>    
  8. #include<netinet/in.h>    
  9. #include<string.h>    
  10.     
  11.     
  12. #define ERR_EXIT(m) \    
  13.     do { \    
  14.         perror(m); \    
  15.         exit(EXIT_FAILURE); \    
  16.     } while (0)    
  17.     
  18.     
  19.     
  20.     
  21. int main(void)    
  22. {    
  23.     int sock;    
  24.     if ((sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)    
  25.         //  listenfd = socket(AF_INET, SOCK_STREAM, 0)    
  26.         ERR_EXIT("socket error");    
  27.     
  28.     
  29.     struct sockaddr_in servaddr;    
  30.     memset(&servaddr, 0, sizeof(servaddr));    
  31.     servaddr.sin_family = AF_INET;    
  32.     servaddr.sin_port = htons(8080);    
  33.     servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");    
  34.     /* inet_aton("127.0.0.1", &servaddr.sin_addr); */    
  35.     
  36.     if (connect(sock, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0)    
  37.         ERR_EXIT("connect error");    
  38.     struct sockaddr_in localaddr;    
  39.     char cli_ip[20];    
  40.     socklen_t local_len = sizeof(localaddr);    
  41.     memset(&localaddr, 0, sizeof(localaddr));    
  42.     if( getsockname(sock,(struct sockaddr *)&localaddr,&local_len) != 0 )    
  43.         ERR_EXIT("getsockname error");    
  44.     inet_ntop(AF_INET, &localaddr.sin_addr, cli_ip, sizeof(cli_ip));    
  45.     printf("host %s:%d\n", cli_ip, ntohs(localaddr.sin_port));     
  46.     
  47.     fd_set rset;    
  48.     FD_ZERO(&rset);    
  49.     int nready;    
  50.     int maxfd;    
  51.     int fd_stdin = fileno(stdin); //    
  52.     if (fd_stdin > sock)    
  53.         maxfd = fd_stdin;    
  54.     else    
  55.         maxfd = sock;    
  56.     char sendbuf[1024] = {0};    
  57.     char recvbuf[1024] = {0};    
  58.         
  59.     while (1)    
  60.     {    
  61.     
  62.         FD_SET(fd_stdin, &rset);    
  63.         FD_SET(sock, &rset);    
  64.         nready = select(maxfd + 1, &rset, NULL, NULL, NULL); //select返回表示检测到可读事件    
  65.         if (nready == -1)    
  66.             ERR_EXIT("select error");    
  67.     
  68.         if (nready == 0)    
  69.             continue;    
  70.     
  71.         if (FD_ISSET(sock, &rset))    
  72.         {    
  73.     
  74.             int ret = read(sock, recvbuf, sizeof(recvbuf));     
  75.             if (ret == -1)    
  76.                 ERR_EXIT("read error");    
  77.             else if (ret  == 0)   //服务器关闭    
  78.             {    
  79.                 printf("server close\n");    
  80.                 break;    
  81.             }    
  82.     
  83.             fputs(recvbuf, stdout);    
  84.             memset(recvbuf, 0, sizeof(recvbuf));    
  85.         }    
  86.     
  87.         if (FD_ISSET(fd_stdin, &rset))    
  88.         {    
  89.     
  90.             if (fgets(sendbuf, sizeof(sendbuf), stdin) == NULL)    
  91.                 break;    
  92.     
  93.             write(sock, sendbuf, strlen(sendbuf));    
  94.             memset(sendbuf, 0, sizeof(sendbuf));    
  95.         }    
  96.     }    
  97.     
  98.     close(sock);    
  99.     return 0;    
  100. }    
赐教!


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/384101.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

BZOJ 2844 | HYSBZ - 2844albus就是要第一个出场——线性基

【题目描述】 BZOJ 2844 | HYSBZ - 2844albus 【题目分析】 题目的意思大概是给一个数列&#xff0c;他有2n个子集&#xff0c;每个子集的元素的异或和构成新的一个数列&#xff0c;排序后问数字Q在这个序列里面的下标。 假如题目是求所有元素的异或和构成一个集合就好弄了&…

CodeForces - 641ELittle Artem and Time Machine——map+树状数组

【题目描述】 CodeForces - 641ELittle Artem and Time Machine 【题目分析】 题目的意思大概是有三种操作 1.在时间t加入一个数字x 2.在时间t删除一个数字x 3.询问在时间t集合里面x的个数 虽然题目描述很简单&#xff0c;但是t和x的范围都是109&#xff0c;我一开始想到的是主…

I/O多路转接之poll 函数

http://blog.csdn.net/li_ning_/article/details/52167224 poll 一、poll()函数&#xff1a; 这个函数是某些Unix系统提供的用于执行与select()函数同等功能的函数&#xff0c;自认为poll和select大同小异&#xff0c;下面是这个函数的声明&#xff1a; [cpp] view plaincopy …

链表相关笔试面试题

1.判断两个链表是否相交 两个链表是否相交可分为以下几种情况     &#xff08;1&#xff09;两个链表都不带环&#xff0c;此时两个链表所对应的最后一个节点是相等的     &#xff08;2&#xff09;两个链表一个带环&#xff0c;一个不带环&#xff0c;两个链表一定…

Linux经典问题—五哲学家就餐问题

http://m.blog.csdn.net/aspenstars/article/details/70149038 一、问题介绍 由Dijkstra提出并解决的哲学家进餐问题(The Dinning Philosophers Problem)是典型的同步问题。该问题是描述有五个哲学家共用一张圆桌&#xff0c;分别坐在周围的五张椅子上&#xff0c;在圆桌上有五…

修改之前的myshell使之支持输入输出重定向

1.open函数     这个函数是打开一个文件&#xff08;文件名叫pathname),以 flag 权限打开&#xff0c;flag 包括了以下几种 O_RDONLY&#xff08;只读&#xff09;, O_WRONLY&#xff08;只写&#xff09;, O_RDWR&#xff08;读写&#xff09;&#xff0c;当文件打开成…

HDU - 6621 K-th Closest Distance——主席树+二分

【题目描述】 HDU - 6621 K-th Closest Distance 【题目分析】 因为看到第kkk大的要求&#xff0c;刚开始的时候一直都在想怎么运用第kkk大来解决问题&#xff0c;但是后来看其他人的博客才发现并不需要用第k大&#xff0c;但是主席树维护权值线段树还是需要的&#xff0c;这…

链表相关的算法题大汇总 — 数据结构之链表奇思妙想

http://blog.csdn.net/lanxuezaipiao/article/details/22100021基本函数&#xff08;具体代码实现见后面&#xff09; 1&#xff0c;构造节点 //定义节点类型 struct Node { int value; Node*next; }; 2&#xff0c;分配节点 //之所以要分配节点原因是需要在分配函数中…

CodeForces - 372CWatching Fireworks is Fun+DP+单调队列优化

【题目描述】 CodeForces - 372CWatching Fireworks is Fun 题目的大概意思就是在一个编号为1…n的街道上现在按照时间顺序放烟花&#xff0c;每个烟花获得的幸福感为b−abs(a−x)b-abs(a-x)b−abs(a−x)&#xff0c;x为观看烟花的位置&#xff0c;为了提升我们的幸福感&#x…

双向链表的基本操作

1.双向链表的数据结构 typedef char DLinkType;typedef struct DLinkNode { DLinkType data; struct DLinkNode* next; struct DLinkNode* prev; }DLinkNode; 双向带头结点的链表有三个成员&#xff0c; 一个是数据&#xff0c; 一个是指针 next 指向当前结点的下一个结点&…

匿名管道

1.进程通信的目的 (1) 数据传输: 一个进程需要将它的数据传输给另一个进程     (2) 资源共享: 多个进程之间共享同样的资源     (3) 通知事件: 一个进程需要向另一个或一组进程发送消息, 通知它们发生了什么事情 2.管道 管道是一种进程之间通信的一种方式, 我们把从…

Currency Exchange——最短路Bellman-Ford算法

【题目描述】 Several currency exchange points are working in our city. Let us suppose that each point specializes in two particular currencies and performs exchange operations only with these currencies. There can be several points specializing in the sam…

C++实现String类

http://blog.csdn.net/randyjiawenjie/article/details/6709539 C实现String类&#xff0c;还没有完成&#xff0c;待继续。 有以下注意的点&#xff1a; &#xff08;1&#xff09;赋值操作符返回的是一个MyString&&#xff0c;而重载的返回的是一个MyString。其中的原因…

POJ 3370 Halloween treats——鸽巢原理+思维

【题目描述】 POJ 3370 Halloween treats Description Every year there is the same problem at Halloween: Each neighbour is only willing to give a certain total number of sweets on that day, no matter how many children call on him, so it may happen that a chi…

将信号量代码生成静态库以及动态库

1.信号量相关代码生成静态库 2.信号量相关代码生成动态库

Wormholes——Bellman-Ford判断负环

【题目描述】 While exploring his many farms, Farmer John has discovered a number of amazing wormholes. A wormhole is very peculiar because it is a one-way path that delivers you to its destination at a time that is BEFORE you entered the wormhole! Each of…

C++11 标准新特性:Defaulted 和 Deleted 函数

https://www.ibm.com/developerworks/cn/aix/library/1212_lufang_c11new/index.html Defaulted 函数 背景问题 C 的类有四类特殊成员函数&#xff0c;它们分别是&#xff1a;默认构造函数、析构函数、拷贝构造函数以及拷贝赋值运算符。这些类的特殊成员函数负责创建、初始化、…

顺序表实现栈相关操作

1.栈的相关概念 栈是一种特殊的线性表, 其中只允许在固定的一端进行插入和删除元素.进行数据插入和删除的一端叫做栈顶, 另一端成为栈底. 不含任何元素的栈称为空栈, 栈又称为先进先出的线性表. 2. 顺序栈的结构 3. 顺序栈的具体操作 (1). 数据结构 typedef char SeqStackTyp…

MPI Maelstrom——Dijkstra

【题目描述】 BIT has recently taken delivery of their new supercomputer, a 32 processor Apollo Odyssey distributed shared memory machine with a hierarchical communication subsystem. Valentine McKee’s research advisor, Jack Swigert, has asked her to bench…

双向带环带头结点的链表实现栈

1. 数据结构 利用带头结点带环的结点实现栈的相关操作.因此, 每一个结点包括了一个前驱, 一个后继, 还有一个数据成员 typedef char DLinkStackType;typedef struct DLinkStack {DLinkStackType data;struct DLinkStack* next;struct DLinkStack* prev; }DLinkStack;2. 初始化…