IO多路复用之select

系统提供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 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#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;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;
}

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

服务器端

IO多路复用之select

客户端

IO多路复用之select

 

select模型的缺点:

1.我的机器测试

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

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

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


当前名称:IO多路复用之select
本文地址:http://hbruida.cn/article/gjschs.html