溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

IO多路復(fù)用之select

發(fā)布時間:2020-07-18 16:08:30 來源:網(wǎng)絡(luò) 閱讀:715 作者:程紅玲OOO 欄目:網(wǎng)絡(luò)安全

系統(tǒng)提供select函數(shù)來實現(xiàn)多路復(fù)用輸入/輸出模型。

select函數(shù)讓我們的程序監(jiān)視多個文件描述符的狀態(tài)變化。程序會停在select這里等待,直到被監(jiān)視的文件描述符中有一個或多個發(fā)生了狀態(tài)變化


函數(shù)原型如下:

IO多路復(fù)用之select

返回值:   成功返回就緒描述符的個數(shù),超過timeout時間且沒有任何事件發(fā)生返回0,失敗返回-1

參數(shù)解釋:

nfds:    被監(jiān)視的文件描述符中值最大描述符值加1(描述符是從0開始的,描述符0、1、2...nfds-1均將被測試

下面三個參數(shù)readset、writeset和exceptset指定我們要讓內(nèi)核測試讀、寫和異常條件的描述符。如果對某一個的條件不感興趣,就可以把它設(shè)為空指針。

readfds:   讀描述符集(所有關(guān)心讀事件的描述符被用戶添加進來),這個參數(shù)是個輸入輸出型參數(shù)

writefds:  寫描述符集

exceptfds: 異常描述符集

struct fd_set可以理解為一個集合,這個集合中存放的是文件描述符,可通過以下四個宏進行設(shè)置:

 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:   設(shè)置一段時間

        (1)timeout=NULL時,當(dāng)前進程一直等待,直到有一個描述符準(zhǔn)備好I/O(永遠等下去)

        (2)timeout指向timeval結(jié)構(gòu)體且時間設(shè)置不為0時,當(dāng)前進程掛起,如果到了設(shè)定的時間(timeout)還沒有任何事件發(fā)生就返回0,進程繼續(xù)執(zhí)行后面的代碼(等待一段固定時間)

        (3)timeout指向timeval結(jié)構(gòu)體且時間設(shè)置為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;
}


運行結(jié)果:(可把服務(wù)器端和客戶端結(jié)果對比起來看)

服務(wù)器端

IO多路復(fù)用之select

客戶端

IO多路復(fù)用之select


 

select模型的缺點:

1.我的機器測試

printf("fd_set=%d\n",sizeof(fd_set));  值為128,說明能監(jiān)視的描述符的最大值為128*8=1024個

2.每次都要把所關(guān)心的描述符重新添加一次,即從用戶態(tài)拷貝到內(nèi)核態(tài)(因為select返回時沒有發(fā)生事件的描述符將被清空),如果描述符較多的時候,開銷是很大的

3.當(dāng)select返回的時候,即使只有一個事件發(fā)生了,也要把整個集遍歷一次,同樣,如果描述符較多的時候,開銷也很大


向AI問一下細節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI