多路IO复用模型select函数使用实例

linux中IO多路复用技术主要有三种:select 、poll 、epoll。

1. 什么是多路IO复用:

其实就是通过一种机制,可以监视多个描述符(Linux中一切皆文件,打开一个文件就需要一个文件描述符,同样,在套接字中建立一个连接也需要一个描述符,这个描述符在计算机中是有个数限制的),一旦某个描述符可读或可写或某些我们需要的状态发生变化,则这种机制就能通知程序,从而进行读写或其它操作。

本节只做select函数说明,poll、epoll将在之后说明

2. 网络多路IO复用模型之select

使用select可以完成非阻塞方式工作的程序,它能够监测需要被监视的文件描述符的变化情况---读写或者异常。但是select监视文件描述符的个数是固定的(1024/2048)。

select函数原型:

 int  select(int nfd, fd_set *readfd, fd_set *writefd,fd_set *errfd, struct timeval *timeout);

maxfd :  指明需要监视所有文件描述符的最大值,并且要+1

readfd:   是一个指向fd_set结构的指针,fd_set可以理解为一个集合,该集合中存放一些文件描述符,这个变量里存放了一些要监视读变化的描述符。

writefd:  同readfd,只不过,这里的文件描述符监视的是写变化的(如果有一个文件可写,则select返回大于0的数)

errfd:  同理,监视发生错误的文件描述符号

timeout:   设置超时时间,主要有三种状态,A.传入NULL,一直阻塞,直到状态变化  B.传入0,则一直返回,无变化为0,有变化为正值,C.大于0,则是超时等待的时间,状态无变化,则超时返回,返回值为0,否则为正值

3. 示例,使用select提高服务器处理能力(服务端代码)

  int func_select(int serverfd)
 {//serverfd为服务端套接字描述符,该函数实现服务端监听客户端连接,并监听已连接客户端的发送数据
     fd_set client_fdset;        //监听文件描述符集合
     int maxsock = serverfd;     //文件描述符中最大文件描述符号
     struct timeval timeout;     //超时时间
     int client_fd[5] = {0};     //已连接客户端套接字
     int connectcount = 0;       //描述符数量
     char buff[1024];
     int ret = 0;
     const int connecte = 10;    //可连接最大数

     while(1)
     {
         FD_ZERO(&client_fdset);             //初始化集合
         FD_SET(serverfd,&client_fdset);     //将该描述符加入到描述符集合中
         timeout.tv_sec = 30;                //设置select超时时间
         timeout.tv_usec = 0;

         for(int i = 0;i<connecte;i++)       //将可能存在的描述符加入监听
         {
             if(client_fd[i] != 0)
            {
                 FD_SET(client_fd[i],&client_fdset);
             }
         }

         /*select只监听可读状态*/
         ret = select(maxsock+1,&client_fdset,NULL,NULL,&timeout);
         if(ret == -1)
             break;
         else if(ret == 0)
             continue;

         //遍历已经连接描述符,检查是否有可都状态
         for(int i = 0;i<connectcount;i++)
         {
             if(FD_ISSET(client_fd[i],&client_fdset))  //判断当前描述符是否加入监视集合
             {
                 ret = recv(client_fd[i],buff,1024,0);
                 if(ret <= 0)
                 {
                     close(client_fd[i]);
                     FD_CLR(client_fd[i],&client_fdset);
                     client_fd[i] = 0;
                 }
                 else
                 {
                     printf("%d -->recv data %s\n",i,buff);
                 }
            }
         }

         if(FD_ISSET(serverfd,&client_fdset)) //检查是否有新的连接
         {
             struct sockaddr_in  addr_cl;
             size_t  socksize = sizeof(struct sockaddr_in);
             int sockclient = accept(serverfd,(struct sockaddr*)&addr_cl,&socksize);
             if(sockclient <= 0)
             {
                 printf("accept err!");
                 continue;
             }

             if(connectcount < connecte)
             {
                 client_fd[connectcount++] = sockclient; //保存新连接,方便加入描述符集合
                 bzero(buff,1024);
                 strcpy(buff,"this is server! welcome!\n");
                 send(sockclient,buff,1024,0);

                 bzero(buff,sizeof(buff));
                 ret = recv(sockclient,buff,1024,0);
                 if(ret < 0)
                 {
                     printf("recv error!");
                     close(sockclient);
                     return 0;
                 }
                 printf("recv data:%s\n",buff);

                 if(sockclient > maxsock)
                 {
                     maxsock = sockclient;   //记录最大套接字描述符
                 }
                 else
                 {
                     printf("max connect ::quit:::\n");
                 }
             }
         }
     }
     
     return 0;
 }

上面代码主要介绍了如何使用select函数来实现服务端的并发能力,当用户调用了select,则当前进程被阻塞,内核会检测select集合中的套接字描述符,如果有任何一个要监听的状态发生变化,则select会返回。

select的优势并不是对单个连接处理的更快,所以,连接数太少的化,性能可能比多线程阻塞IO更低,但是它的优点是处理很多连接。

select的优点:
1.可以监视多个文件描述符,减少了平均等待时间
2.处理大批量连接时,有效减轻进程调度的压力
select缺点:
1.监听文件描述符有上限,这个上限是由fd_set决定的。
2.它返回的只是就绪事件的个数,要判断是那个事件满足,需要遍历文件描述符。
3.select监听的集合是输入输出参数,每次监听都需要重新初始化。
4.每次调用select,都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多时会很大
5.内核采用遍历fd集合的方式来检测就绪事件,这个开销在fd很多时也很大
6.select和poll都只能工作在低效的LT(水平触发)模式

 

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值