select()与poll()实现多路复用

1. 多路复用I/O

1)应用程序处理多路输入输出流,若采用阻塞模式,将得不到预期的目的;

2)若采用阻塞模式,对多个输入进行轮询,会非常浪费CPU时间;

3)若设置多个进程,分别处理一条数据通路,将产生进程间的同步与通信问题,使程序变得非常复杂;

4)比较好的方法是使用I/O多路复用,其基本思想:

先构造一张有关描述符的表,然后调用一个函数,当这些文件描述符中的一个或多个已准备好进行I/O时函数才返回。函数返回时告诉进程哪个描述符已就绪,可以进行I/O操作。

2. select()/poll()实现多路复用

头文件:#include <sys/select.h>

       #include <sys/time.h>

       #include <sys/types.h>

       #include <unistd.h>

函数原型:int select(int nfds, fd_set *readfds,fd_set *writefds,fd_set *exceptfds, struct timeval *timeout);

头文件:#include <poll.h>

函数原型:int poll(struct pollfd *fds, nfds_tnfds, int timeout);

注:本博文只学习select函数,poll函数用法select函数类似。

参数:

maxfd

所有监控的文件描述符中最大的那个加1

read_fds

所有要读的文件描述符的集合

write_fds

所有要写的文件描述符的集合

except_fds

其他要向我们通知的文件描述符

Timeout

超时设置

NULL: 一直阻塞就,直到有文件描述符就绪或出错

时间值为0:仅仅检测文件描述符的状态,然后立即返回,非阻塞模式

时间值不为0:在指定的时间内,如果没有事件发生,则超时返回。

3. select函数特性

在调用select函数时进程会一直阻塞直到以下某种情况发生:

1)有文件可以读

2)有文件可以写

3)超时所设置的时间到了

为了设置文件描述符,用到以下几个函数:

void FD_CLR(int fd, fd_set *set);fdfdset里面清除

int FD_ISSET(int fd, fd_set *set);判断fd是否在fdset集合中

void FD_SET(int fd, fd_set *set);fd加入到fdset

void FD_ZERO(fd_set *set);fdset中清除所有的文件描述符

4. 从具体例程中学习select函数应用

/*************************************************************************

 @Author: wanghao

 @Created Time : Fri 29 May 2018 00:00:00 PM PDT

 @File Name: server.c

 @Description:

 ************************************************************************/

#include <stdio.h>

#include <sys/types.h>     

#include <sys/socket.h>

#include <sys/socket.h>

#include <netinet/in.h>

#include <arpa/inet.h>

#include <unistd.h>

#include <string.h>

#include <sys/select.h>

#include <sys/time.h>

#include <sys/types.h>

#include <unistd.h>

#include <sys/select.h>     

#define N 36

#define MAXSIZE 24

#define NUM 24

int main(int argc, const char *argv[])

{

       intsocketfd;

       intclient_fd;

       intlistenfd;

       intmaxfd;

       intdatalen;

       intaddrlen;

       inti;

       fd_setrfds;

       fd_settempfd;

       u_shortport;

       pid_tpid;

       charbufread[MAXSIZE] = {0};

       charbufwrite[MAXSIZE] = {0};

       structsockaddr_in my_addr;

       structsockaddr_in client_addr;

       if(argc< 3)

       {

              printf("usage:%s <IPadd> <Port>\n",argv[0]);

              return-1;

       }

       listenfd= socket(PF_INET, SOCK_STREAM, 0);

       if(socket< 0)

       {

              perror("");

              return-1;

       }

       printf("socket= %d\n",listenfd);

       port= (u_short)atol(argv[2]);

       printf("port= %d\n", port);

       my_addr.sin_family= PF_INET;

       my_addr.sin_port= htons(port);

       my_addr.sin_addr.s_addr= inet_addr(argv[1]);

       addrlen= sizeof(struct sockaddr_in);

       if(bind(listenfd,(struct sockaddr *)(&my_addr),addrlen) < 0)

       {

              perror("");

              return-2;

       }

       listen(listenfd,N);

       /*初始化rfds文件描述符集合*/

       FD_ZERO(&rfds);

/*初始化tempfd文件描述符集合*/

       FD_ZERO(&tempfd);

       /*listenfd加入rfds文件描述符集合中*/

       FD_SET(listenfd,&rfds);

       /*初始化最大文件描述符*/

       maxfd= listenfd;

       while(1)

       {

       /*因为在文件描述符集合中,select每执行一次,文件描述符就会减少一个,所以必须定义一个临时变量,用于select。比如文件描述符集合中有文件描述符345,当3代表的进程发生时,文件描述符集合会将3去掉此时剩下45。在下一次循环中,如何保证文件描述符集合还是包括345是完整的,最有效的方法就是定义一个临时文件描述符集合*/

              tempfd= rfds;

              if(select(maxfd+1,&tempfd, NULL, NULL, NULL) <= 0)

              {

                     perror("failto select");

                     return-3;

              }

              /*查找是哪个文件描述符促发select执行*/

              for(i= 0; i <= maxfd; i++)

              {

                     if(FD_ISSET(i,&tempfd))

                     {

                            /*是否是listenfd,即又有客户端要连接进来*/

                            if(i== listenfd)

                            {

                                   if((client_fd= accept(listenfd,(struct sockaddr *)(&client_addr),&addrlen)) < 0)

                                   {

                                          perror("");

                                          return-4;

                                   }

                                   /*如果有客户端连接进来,首先将它加入文件描述符集合中*/

                                   FD_SET(client_fd,&rfds);

                                   /*如果新的客户端fd大于maxfd,则用它替代maxfd*/

                                   if(client_fd> maxfd)

                                   {

                                          maxfd= client_fd;

                                   }

                            }

                            /*肯定是客户端发送消息过来*/

                            else

                            {

                                   datalen= read(i, bufread, sizeof(bufread));

                                   if(datalen<= 0)

                                   {

                                          close(i);

                                          continue;

                                   }

                                   bufread[datalen]= '\0';

                                   printf("%s\n",bufread);

                            }

                     }

              }

       }

       close(listenfd);

       return0;

}

猜你喜欢

转载自blog.csdn.net/weixin_42048417/article/details/80559176