千家信息网

IO多路复用之select

发表于:2024-11-21 作者:千家信息网编辑
千家信息网最后更新 2024年11月21日,系统提供select函数来实现多路复用输入/输出模型。select函数让我们的程序监视多个文件描述符的状态变化。程序会停在select这里等待,直到被监视的文件描述符中有一个或多个发生了状态变化函数原
千家信息网最后更新 2024年11月21日IO多路复用之select

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

select函数让我们的程序监视多个文件描述符的状态变化。程序会停在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 #include #include #include #include #include #include #include #include #define BLOCK 6int 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;i0){                    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;i0 && 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 #include #include #include #include #include #include #include #include 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;}


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

服务器端

客户端


 

select模型的缺点:

1.我的机器测试

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

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

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


0