Linux--高级I/O多路复用之select

1. select是做什么的?

系统提供select函数来实现多路复用输入/输出模型。select系统调用是用来让我们的程序监视**多个文件句柄**的状态变化的。程序会停在select这里等待,直到被监视的文件句柄有一个或多个发生了状态改变。

2. select函数原型?

 ![这里写图片描述](https://img-blog.csdn.net/20170606093025905?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvc2F5aGVsbG9fd29ybGQ=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
 这里有几个参数。
 1.参数nfds是需要监视的最大的文件描述符值+1。

2.参数rdset,wrset,exset分别对应于需要检测的可读文件描述符的集合,可写文件描述符的集合及异常文件描述符的集合。 
如果对某一个的条件不感兴趣,就可以把它设为空指针。struct fd_set可以理解为一个集合,这个集合中存放的是文件描述符,可通过以下四个宏进行设置:
    void FD_ZERO(fd_set *fdset);           //清空集合
    void FD_SET(int fd, fd_set *fdset);   //将一个给定的文件描述符加入集合之中
    void FD_CLR(int fd, fd_set *fdset);   //将一个给定的文件描述符从集合中删除
    int FD_ISSET(int fd, fd_set *fdset);   // 检查集合中指定的文件描述符是否可以读写 

3.timeout告知内核等待所指定描述字中的任何一个就绪可花多少时间。其timeval结构用于指定这段时间的秒数和微秒数。

struct timeval{
          long tv_sec;   //seconds
          long tv_usec;  //microseconds
       };

这个参数有三种选项:
(1)永远等待下去:仅在有一个描述字准备好I/O时才返回。为此,把该参数设置为空指针NULL。
(2)等待一段固定时间:在有一个描述字准备好I/O时返回,但是不超过由该参数所指向的timeval结构中指定的秒数和微秒数。
(3)根本不等待:检查描述字后立即返回,这称为轮询。为此,该参数必须指向一个timeval结构,而且其中的定时器值必须为0。

select特点:

1、可监控的文件描述符个数取决与sizeof(fd_set)的值。本人服务器上sizeof(fd_set)=512,每bit表示一个文件描述符,服务器上支持的最大文件描述符是4096(512*8)。据说可调,另有说虽然可调,但调整上限受于编译内核时的变量值。

2、将fd加入select监控集的同时,还要使用一个数据结构array保存放到select监控集中的fd。
一是用于在select返回后,array作为源数据和fd_set进行FD_ISSET判断。
二是select返回后会把以前加入的但并无事件发生的fd清空,则每次开始select前都要重新从array取得fd逐一加入(FD_ZERO最先),扫描array的同时取得fd最大值maxfd,用于select的第一个参数。

3、select模型必须在select前循环array(加fd,取maxfd),select返回后循环array(FD_ISSET判断是否有时间发生)。

select缺点:

(1)每次调用select,都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多时会很大。
(2)同时每次调用select都需要在内核遍历传递进来的所有fd,这个开销在fd很多时也很大。
(3)select支持的文件描述符数量太小了,默认是1024。

测试程序:

server端:

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

int array_fds[1024];

int startup(char *ip,int port)
{
    int sock = socket(AF_INET,SOCK_STREAM,0);
    if(sock < 0)
    {
        perror("socket");
        exit(1);
    }
    int flg = 1;
    setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&flg,sizeof(flg));

    struct sockaddr_in local;
    local.sin_family = AF_INET;
    local.sin_port = htons(port);
    local.sin_addr.s_addr = inet_addr(ip);

    if(bind(sock,(struct sockaddr*)&local,sizeof(local)) < 0){
        perror("bind");
        exit(3);
    }

    if(listen(sock,10)<0){
        perror("listen");
        exit(4);
    }
    return sock;
}

int main(int argc ,char* argv[])
{
    if(argc != 3)
    {
        printf("usage: %s [ip] [port]\n",argv[0]);
        return 1;
    }

    int maxfd = 0;
    int listenSock = startup(argv[1],atoi(argv[2]));

    fd_set rfds;
    array_fds[0] = listenSock;
    int array_size = sizeof(array_fds)/sizeof(array_fds[0]);
    int i = 1;
    for(;i<array_size;++i)
        array_fds[i] = -1;

    while(1)
    {
        struct timeval timeout = {0,0};
        FD_ZERO(&rfds);
        maxfd = -1;
        for(i = 0;i<array_size;++i)
        {
            if(array_fds[i] > 0){
                FD_SET(array_fds[i],&rfds);
                if(array_fds[i] > maxfd)
                    maxfd = array_fds[i];
            }
        }

    switch(select(maxfd+1,&rfds,NULL,NULL,NULL))//这里最后一个参数为timeout 则为等待一段时间后退出,若为NULL则表示阻塞等待
    {
        case 0:
            printf("timeout...\n");
            break;
        case -1:
            perror("select");
            break;
        default:
            {
                int j = 0;
                for(;j<array_size;++j)
                {
                    if(array_fds[j] < 0)
                        continue;
                        //j==0表示listensock响应
                    if(j == 0 && FD_ISSET(array_fds[j],&rfds)){
                        struct sockaddr_in client;
                        socklen_t len = sizeof(client);
                        int new_fd = accept(array_fds[j],\
                                            (struct sockaddr*)&client,&len);
                        if(new_fd < 0){
                            perror("accept");
                            continue;
                        }else{
                            printf("get a new client:{%s:%d}\n",\
                                    inet_ntoa(client.sin_addr),\
                                    ntohs(client.sin_port));
                            int k = 1;
                            for(;k<array_size;++k){
                                if(array_fds[k] < 0){
                                    array_fds[k] = new_fd;
                                    break;
                                }
                            }
                        if(k == array_size){
                            close(new_fd);
                        }
                    }
                }
                //j!=0表示其他的响应
                else if(j!=0 && FD_ISSET(array_fds[j],&rfds)){
                    char buf[10240];
                    ssize_t s = read(array_fds[j],\
                                     buf,sizeof(buf)-1);
                    if(s > 0){
                        buf[s] = 0;
                        printf("client say :%s\n",buf);
                    }else if(s == 0){
                        printf("client quit~\n");
                        close(array_fds[j]);
                        array_fds[j] = -1;
                    }else{
                        perror("read");
                        close(array_fds[j]);
                        array_fds[j] = -1;
                    }   
                }
            }
        break;
    }
  }
 }
}

实验结果:
这里写图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值