美文网首页
多路IO复用技术--select

多路IO复用技术--select

作者: 精简好文 | 来源:发表于2019-12-15 10:03 被阅读0次

    1.功能介绍

    1.1 网络通信中,对于套接字(文件描述符)在任意时刻是否有数据可读,我们不知道,只会用while 10毫秒循环收发,select能够解决这个问题,时时监听套接字的读写情况,有收到数据就读取。

    2.相关函数说明

    2.1 int select(int n,fd_set * readfds,fd_set * writefds,fd_set * exceptfds,struct timeval * timeout);
    说明:select()用来等待文件描述符状态的改变。参数n代表最大的文件描述符加1,参数readfds、writefds和exceptfds称为文件描述符集,填了readfd代表监听可读事件(收数据),不填代表不监听该事件,其他两个同理,timeout代表超时时间设置,就是个时间,可不设置,填NULL,struct timeval结构体如下:
     struct timeval{      
             long tv_sec;   /*秒 */
    
             long tv_usec;  /*微秒 */   
    
         }
    2.2 字符集相关函数操作
    
    int FD_ZERO(fd_set *fdset);  
    
    int FD_CLR(int fd, fd_set *fdset);   
    
    int FD_SET(int fd, fd_set *fdset);  
    
    int FD_ISSET(int fd, fd_set *fdset);
    说明:FD_ZERO是初始化字符集变量,使用FD_SET将套接字fd(文件描述符)加入字符集fdset,使用FD_CLR将套接字fd(文件描述符)从字符集fdset中删除,使用FD_ISSET来判断套接字fd(文件描述符)是否加入字符集fdset进行监听。
    

    3.使用场景

    3.1 有多个套接字(高并发)需要收发数据。

    4.使用代码

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <sys/types.h>
    #include <unistd.h>
    #include <arpa/inet.h>
    #include <netinet/in.h>
    #include <ctype.h>
    
    int main()
    {
        //创建socket
        int lfd = socket(AF_INET, SOCK_STREAM, 0);
        if(lfd < = 0)
        {
               return -1;
        }
    
        //设置端口复用
        int opt = 1;
        setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
    
        //绑定
        struct sockaddr_in serv;
        serv.sin_family = AF_INET;
        serv.sin_port = htons(8888);
        serv.sin_addr.s_addr = htonl(INADDR_ANY);
        bind(lfd, (struct sockaddr *)&serv, sizeof(serv));
    
        //监听
        listen(lfd, 128);
    
        //定义文件描述符集变量
        fd_set readfds, tmpfds;
        
        //初始化文件描述符集变量
        FD_ZERO(&readfds);
        FD_ZERO(&tmpfds);
        
        //将lfd加入到readfds中
        FD_SET(lfd, &readfds);
        
        int maxfd = lfd;
        int cfd;
        int i;
        int sockfd;
        int nready;
        int n;
        char buf[1024];
        
        sleep(10);
        
        while(1)
        {
            FD_ZERO(&tmpfds);
            tmpfds = readfds;
            
            //将文件描述符集委托给内核监控
            //tmpfds是传入传出参数, 
                //传入:告诉内核要监测哪些文件描述符
                //传出:内核告诉应用程序那些文件发生了变化, 但是并没有具体告诉到底是哪些.
            nready = select(maxfd+1, &tmpfds, NULL, NULL, NULL);
            printf("nready==[%d]\n", nready);
            if(nready<0)
            {
                if(errno==EINTR)//若被信号中断,则errno为EINTR
                {
                    perror("select error");
                    continue;
                }
                close(lfd);
                exit(-1);
            }
            
            //若有客户端连接请求, 则接受新的连接,同时将新的连接加入到文件描述符集中
            if(FD_ISSET(lfd, &tmpfds))
            {
                cfd = accept(lfd, NULL, NULL);
                if(lfd < = 0)
                {
                    continue;
                }
                //将cfd加入到文件描述符集中
                FD_SET(cfd, &readfds);
                
                if(maxfd<cfd)
                {
                    maxfd = cfd;
                }
                
                if(--nready==0)
                {
                    continue;
                }
            }
            
            //下面是有客户端发送数据到来的情况
            for(i=lfd+1; i<=maxfd; i++)
            {
                //判断某个文件描述符是否有变化
                sockfd = i;
                if(FD_ISSET(sockfd, &tmpfds))
                {
                    //读数据
                    memset(buf, 0x00, sizeof(buf));
                    n = Read(sockfd, buf, sizeof(buf));
                    if(n<=0)
                    {
                        //若读数据失败或者对方关闭连接, 则将sockfd从文件描述符集中删除
                        close(sockfd);
                        FD_CLR(sockfd, &readfds);                   
                    }
                    else 
                    {
                        for(int k=0; k<n; k++)
                        {
                            buf[k] = toupper(buf[k]);
                        }
                        Write(sockfd, buf, n);
                    }
                    
                    if(--nready==0)
                    {
                        break;
                    }
                }
            }
        }
        
        close(lfd);
        
        return 0;
    }
    

    5.缺点

    5.1 当客户端多个连接, 但少数活跃的情况, select效率较低。

    例如: 作为极端的一种情况, 3-1023文件描述符全部打开, 但是只有1023有发送数据, select就显得效率低下。

    5.2 fd_set能容纳的文件描述符 数量由FD_SETSIZE指定,一般情况下,FD_SETSIZE等 于1024,这就限制了select能同时处理的(套接字)文件描述符的总量(FD_SETSIZE=1024 fd_set使用了该宏, 当然可以修改内核, 然后再重新编译内核, 一般不建议这么做.)。

    相关文章

      网友评论

          本文标题:多路IO复用技术--select

          本文链接:https://www.haomeiwen.com/subject/opjknctx.html