温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

IO多路复用之select

发布时间:2020-07-18 16:08:30 来源:网络 阅读:698 作者:程红玲OOO 栏目:网络安全

系统提供select函数来实现多路复用输入/输出模型。

select函数让我们的程序监视多个文件描述符的状态变化。程序会停在select这里等待,直到被监视的文件描述符中有一个或多个发生了状态变化


函数原型如下:

IO多路复用之select

返回值:   成功返回就绪描述符的个数,超过timeout时间且没有任何事件发生返回0,失败返回-1

参数解释:

nfds:    被监视的文件描述符中值最大描述符值加1(描述符是从0开始的,描述符0、1、2...nfds-1均将被测试

下面三个参数readset、writeset和exceptset指定我们要让内核测试读、写和异常条件的描述符。如果对某一个的条件不感兴趣,就可以把它设为空指针。

readfds:   读描述符集(所有关心读事件的描述符被用户添加进来),这个参数是个输入输出型参数

writefds:  写描述符集

exceptfds: 异常描述符集

struct fd_set可以理解为一个集合,这个集合中存放的是文件描述符,可通过以下四个宏进行设置:

 void FD_CLR(int fd, fd_set *fdset);   //把给定的文件描述符从指定集合删除

 int FD_ISSET(int fd, fd_set *fdset);  //判断指定描述符时候被加入了指定集合中(是否可读/可写) 

 void FD_SET(int fd, fd_set *fdset);   //把一个给定的文件描述符加入集合之中

 void FD_ZERO(fd_set *fdset);           //清空集合

timeout:   设置一段时间

        (1)timeout=NULL时,当前进程一直等待,直到有一个描述符准备好I/O(永远等下去)

        (2)timeout指向timeval结构体且时间设置不为0时,当前进程挂起,如果到了设定的时间(timeout)还没有任何事件发生就返回0,进程继续执行后面的代码(等待一段固定时间)

        (3)timeout指向timeval结构体且时间设置为0时,不会等待,立即返回(根本不等待)

                      struct timeval{

                                       long tv_sec;   //seconds

                                       long tv_usec;  //microseconds

                               };

server_select.c

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>

#define BLOCK 6

int fds[64];

void usage(char * proc)
{
    printf("%s [ip] [port]\n",proc);
}

int create_sock(char *port,const char * inaddr)
{
    //1.
    int listenfd=socket(AF_INET,SOCK_STREAM,0);
    if(listenfd<-1){
        perror("listenfd");
        exit(1);
    }
    //2.
    struct sockaddr_in local;
    local.sin_family=AF_INET;
    int _port=atoi(port);
    local.sin_port=htons(_port);
    local.sin_addr.s_addr=inet_addr(inaddr);

    struct linger lig;
    int iLen;
    lig.l_onoff=1;
    lig.l_linger=0;
    iLen=sizeof(struct linger);
    setsockopt(listenfd,SOL_SOCKET,SO_LINGER,(char *)&lig,iLen);
    //3.
    if(bind(listenfd,(struct sockaddr*)&local,sizeof(local))<0){
        perror("bind");
        exit(2);
    }
    //4.
    if(listen(listenfd,BLOCK)<0){
        perror("listen");
        exit(3);
    }

    return listenfd;
}

int main(int argc,char* argv[])
{
    if(argc!=3){
        usage(argv[0]);
        exit(1);
    }

    //create listenfd
    int listen_fd=create_sock(argv[2],argv[1]);

    struct sockaddr_in client;
    socklen_t len=sizeof(client);

    int max_fd=listen_fd;

    char buf[1024];

    //initialize fds[]
    int fds_num=sizeof(fds)/sizeof(fds[0]);
    int i=0;
    for(i=0;i<fds_num;++i){
        fds[i]=-1;
    }
    fds[0]=listen_fd;

    int done=0;
    while(!done){
            //define and initialize parameter of select() 
            fd_set readset;
            FD_ZERO(&readset);
            FD_SET(listen_fd,&readset);
    
            fd_set writeset;
            FD_ZERO(&writeset);
    
            struct timeval timeout;
            timeout.tv_sec=5;
            timeout.tv_usec=0;
            
            //
            for(i=1;i<fds_num;++i){
                if(fds[i]>0){
                    FD_SET(fds[i],&readset);
                    FD_SET(fds[i],&writeset);
                    if(fds[i]>max_fd)
                        max_fd=fds[i];
                }
                else
                    break;
            }

        switch(select(max_fd+1,&readset,&writeset,NULL,&timeout)){
        //switch(select(max_fd+1,&readset,NULL,NULL,&timeout)){
            case -1:
                perror("select");
                break;
            case 0:
                printf("timeout...\n");
                break;
            default:
                for(i=0;i<fds_num;++i){
                 //listen_fd happen
                    if(fds[i]==listen_fd && FD_ISSET(fds[i],&readset)){
                        int connfd=accept(listen_fd,(struct sockaddr*)&client,&len);
                        if(connfd<0){
                            perror("accept");
                            break;
                        }else{
                            printf("get a connect...\n");
                            for(i=0;i<fds_num;++i){
                                if(fds[i]==-1){
                                    fds[i]=connfd;
                                    break;
                                }
                            }
                        }
                    }
                    //normal event happen
                    else if(fds[i]>0 && FD_ISSET(fds[i],&readset)){
                            ssize_t _size=read(fds[i],buf,sizeof(buf)-1);
                            if(_size<0){
                                perror("read");
                            }else if(_size==0){//client closed
                                printf("client shutdown...\n");
                                close(fds[i]);
                                fds[i]=-1;
                                continue;
                            }else{
                                buf[_size]='\0';
                                printf("client# %s",buf);
                                if(FD_ISSET(fds[i],&writeset)){
                                    if(write(fds[i],buf,sizeof(buf)-1)<0){
                                        perror("write");
                                    }
                                }
                            }
                            
                    }
                    else{
                    }
                }
                break;
        }
    }
    
  return 0;
}


client_select.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <netinet/in.h>

void usage(char *proc)
{
    printf("%s [ip] [port]\n",proc);
}

int creat_socket()
{
    int fd=socket(AF_INET,SOCK_STREAM,0);
    if(fd<0){
        perror("socket");
        exit(1);
    }   
    return fd;
}

int main(int argc,char* argv[])
{
    if(argc!=3){
        usage(argv[0]);
        exit(1);
    }

    int fd=creat_socket();

    int _port=atoi(argv[2]);
    struct sockaddr_in addr;
    addr.sin_family=AF_INET;
    addr.sin_port=htons(_port);
    inet_aton(argv[1],&addr.sin_addr);
    socklen_t addrlen=sizeof(addr);
    if(connect(fd,(struct sockaddr*)&addr,addrlen)<0){
        perror("connect");
        exit(2);
    }
    
    char buf[1024];
    while(1){
        memset(buf,'\0',sizeof(buf));
        printf("Please Enter:");
        fgets(buf,sizeof(buf)-1,stdin);
        if(send(fd,buf,sizeof(buf)-1,0)<0){
            perror("send");
            continue;
        }

        //回显
        ssize_t _size=recv(fd,buf,sizeof(buf)-1,0);
        if(_size>0){
            buf[_size]='\0';
            printf("echo->%s\n",buf);
        }
    }   

    return 0;
}


运行结果:(可把服务器端和客户端结果对比起来看)

服务器端

IO多路复用之select

客户端

IO多路复用之select


 

select模型的缺点:

1.我的机器测试

printf("fd_set=%d\n",sizeof(fd_set));  值为128,说明能监视的描述符的最大值为128*8=1024个

2.每次都要把所关心的描述符重新添加一次,即从用户态拷贝到内核态(因为select返回时没有发生事件的描述符将被清空),如果描述符较多的时候,开销是很大的

3.当select返回的时候,即使只有一个事件发生了,也要把整个集遍历一次,同样,如果描述符较多的时候,开销也很大


向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI