linux I/O复用-------select

select系统调用的用途是:在一段时间内,监听当用户感兴趣的文件描述符上的可读,可写,可异常等事件。

《一》select系统调用原型:

Int select(int nfds,fd_set* readfds,fd_set* writefds,fd_set* exceptfds,struct timeval* time out);

成功将返回就绪文件描述符的个数,失败返回-1

  1. nfds:监听的最大文件描述符的值+1(原因:它是轮询检测的)
  2. readfds,writefds,exceptfds分别是可读,可写,异常事件,(select调用返回时,内核修改他们来通知应用程序哪些文件描述符已经就绪)

                        fd_set是一个整型数组,该数组的每一位都标记一个文件描述符,

                        fd_set能容纳的文件描述符数量由FD_SETSIZE指定1024

   3 .timeout:超时时间 以微秒记

select成功时返回就绪(可读,可写和异常)文件描述符的总数,如果在超时时间内没有任何文件描述符就绪,select返回0.

《二》select的原理图如下:

select

《三》select调用的代码:

客户端:

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

void main()
{
    int sockfd=socket(AF_INET,SOCK_STREAM,0);
    assert(sockfd!=-1);

    struct sockaddr_in ser,cli;
    ser.sin_family=AF_INET;
    ser.sin_port=htons(6000);
    ser.sin_addr.s_addr=inet_addr("127.0.0.1");

    
    int n=connect(sockfd,(struct sockaddr*)&ser,sizeof(ser));
    assert(n!=-1);

    while(1)
    {
        char buff[128]={0};
        printf("input:\n");
        fgets(buff,128,stdin);
        
        if(strncmp(buff,"end",3)==0)
        {
            close(n);
            break;
        }
        send(sockfd,buff,strlen(buff),0);
        memset(buff,0,128);
        recv(sockfd,buff,127,0);
        printf("%s\n",buff);
    }
    close(sockfd);
}

服务器:

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

void main()
{
    int listenfd=socket(AF_INET,SOCK_STREAM,0);
    assert(listenfd!=-1);
    struct sockaddr_in ser,cli;
    ser.sin_family=AF_INET;
    ser.sin_port=htons(6000);
    ser.sin_addr.s_addr=inet_addr("127.0.0.1");



    int res=bind(listenfd,(struct sockaddr*)&ser,sizeof(ser));
    assert(res!=-1);

    listen(listenfd,5);
    fd_set reads;

    int fds[10];
    int p=0;
    for(;p<10;p++)
    {
        fds[p]=-1;
    }

    fds[0]=listenfd;

    while(1)
    {
        FD_ZERO(&reads);
        int maxfd=-1;
        int i=0;
        for(;i<10;++i)
        {
            if(fds[i]!=-1)
            {
                FD_SET(fds[i],&reads);
                if(fds[i]>maxfd)
                {
                    maxfd=fds[i];
                }
            }
        }
        int n=select(maxfd+1,&reads,NULL,NULL,NULL);
        printf("select back\n");
        if(n==-1)
        {
            printf("select error\n");
            break;
        }
        else
        {
            for(i=0;i<10;i++)
            {
                if(fds[i]!=-1 && FD_ISSET(fds[i],&reads))
                {
                    if(fds[i]==listenfd)
                    {
                        int len=sizeof(cli);
                        int c=accept(listenfd,(struct sockaddr*)&cli,&len);
                        printf("one cli link seccess\n");
                        int j=0;
                        for(;j<10;++j)
                        {
                            if(fds[j]==-1)
                            {
                                fds[j]=c;
                                break;
                            }
                        }
                    }
                    else
                    {
                        char buff[128]={0};
                        int num=recv(fds[i],buff,127,0);
                        if(num<=0)
                        {
                            printf("one client break link\n");
                            close(fds[i]);
                            fds[i]=-1;
                            continue;
                        }
                        printf("%d: %s\n",fds[i],buff);
                        send(fds[i],"ok",2,0);
                    }
                }
            }

        }
    }
}

《四》Select的优缺点:

  1. 关注的 文件描述符的值的大小和文件描述符的个数受限制,值最大是1023,文件描述符最多有1024个,受宏FD_SETSIZE限制
  2. 会有两次拷贝
  3. Fd_set仅仅是一个文件描述符的集合,并没有将文件描述符和事件绑定,需要传入3中事件,所以Select不能处理更多类型的事件
  4. 由于内核对fd_set集合的在线修改,导致应用程序下次调用select前必须要重置fd_set集合
  5. Select调用都返回整个的事件集合(其中就包括就绪和未就绪的)采用轮询的方式来检测就绪文件描述符,所以索引就绪的文件描述符的时间复杂度为O(n)
  6. Select只能工作在相对低效的LT模式下

猜你喜欢

转载自blog.csdn.net/weixin_41537785/article/details/81178725