I/O多路转接之select

来源:互联网 发布:共享网络无线发射器 编辑:程序博客网 时间:2024/04/29 21:07
    I/O多路转接之select
  作用:是用来让我们的程序监视多个文件句柄的状态变化的。程序会停在select这里等待,直到被监视的文件句柄有一个或多个发生了状态改变。
  
  函数原型:

     中间的三个参数readset、writeset和exceptset指定我们要让内核测试读、写和异常条件的描述字。如果对某一个的条件不感兴趣,就可以把它设为空指针。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);   // 检查集合中指定的文件描述符是否可以读写 

  fd_set结构体是文件描述符集,该结构体实际上是一个整型数组,数组中的每个元素的每一位标记一个文件描述符。fd_set能容纳的文件描述符 数量由FD_SETSIZE指定,一般情况下,FD_SETSIZE等  于1024,这就限制了select能同时处理的文件描述符的总量。

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

         struct timeval{

                   long tv_sec;   //seconds

                   long tv_usec;  //microseconds

       };

这个参数有三种可能:

  1)永远等待下去:仅在有一个描述字准备好I/O时才返回。为此,把该参数设置为空指针NULL。

  2)等待一段固定时间:在有一个描述字准备好I/O时返回,但是不超过由该参数所指向的timeval结构中指定的秒数和微秒数。

  3)根本不等待:检查描述字后立即返回,这称为轮询。为此,该参数必须指向一个timeval结构,而且其中的定时器值必须为0。

函数返回值
 1、执行成功则返回文件描述词状态已改变的个数;
 2、如果返回0代表在描述词状态改变前已超过timeout时间,没有返回;
 3、当有错误发生时则返回-1,错误原因存于errno,此时参数readfds,writefds,exceptfds和 timeout的值变成不可预测。错误值可能为:
EBADF 文件描述词为无效的或该文件已关闭
EINTR 此调用被信号所中断 EINVAL 参数n 为负值。
ENOMEM 核心内存不足

说明:

   1、select只负责等待IO,不负责对IO进行操作,由recv/send等函数进行

   2、select一共有两次系统调用:1)select系统调用 2)recvfrom系统调用

select的优点:
  select模型是Windows sockets中最常见的I/O模型,它利用select实现I/O管理,通过对select函数的调用,应用程序可以判断套接字是否存在数据、能否向该套接字写入数据。
  如:在调用recv函数之前,先调用select函数,如果系统中没有可读的数据,那么select函数就会阻塞在这里。当系统中存在可读和可写的数据时,select函数返回,就可以调用recv函数接受数据了。
  可以看出select模型,需要两次调用函数。第一次调用select函数和第二次调用socket API。使用该模式的1好处是:可以等待多个套接字。

select的缺点:
  1、最大并发数限制:使用32个整数的32位,即32*32=1024来标示fd;
  2、效率低,每次都会线性的扫描整个fd_set,集合越大速度越慢
  3、每次调用select,都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多时会很大。


使用select编写网络服务器
代码:
/**************************************************************************
    >file name:my_select.c
    >author:Comedly
    >create time:2016-8-7
**************************************************************************/

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

int fds[64];
const int fds_nums = sizeof(fds)/sizeof(fds[0]);

static int startup(const char* ip,int port)
{
    int sock = socket(AF_INET,SOCK_STREAM,0);
    if(sock < 0)
    {
        perror("socket()");
        exit(2);
    }

    int opt = 1;
    setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
    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,5) < 0)
    {
        perror("listen()");
        exit(4);
    }
    return sock;
}

static void usage(const char *proc)
{
    printf("%s [ip] [port]\n",proc);
}

int main(int argc,char* argv[])
{
    if(argc != 3)
    {
        usage(argv[0]);
        exit(1);
    }

    int listen_sock = startup(argv[1],atoi(argv[2]));
    fd_set rset;
    int i = 0;
    FD_ZERO(&rset);
    FD_SET(listen_sock,&rset);
    //initial fds
    for(;i < fds_nums;i++)
    {
        fds[i] = -1;
    }
    fds[0] = listen_sock;
    int done = 0;
    while(!done)
    {
        int max_fd = -1;
        for(i = 0;i < fds_nums;i++)
        {
            if(fds[i]>0)
            {
                FD_SET(fds[i],&rset);
                max_fd = max_fd < fds[i]?fds[i]:max_fd;
            }
        }
        switch(select(max_fd+1,&rset,NULL,NULL,NULL))
        {
            case 0:
                printf("time out...\n");
                break;
            case -1:
                perror("select()");
                break;
            default:
                for(i = 0;i < fds_nums;i++)
                {
                    if(i==0 && FD_ISSET(listen_sock,&rset))
                    {
                        //printf("there\n");
                        struct sockaddr_in peer;
                        socklen_t len = sizeof(peer);
                        int newfd = accept(listen_sock,(struct sockaddr*)&peer,&len);
                        if(newfd > 0)
                        {
                            printf("get a new client$ socket->%s:%d\n",inet_ntoa(peer.sin_addr),ntohs(peer.sin_port));
                        }
                        int j = 0;
                        for(j;j<fds_nums;j++)
                        {
                            if(fds[j] == -1)
                            {
                                fds[j] = newfd;
                                break;
                            }
                        }
                        if(j == fds_nums)
                        {
                            close(newfd);
                        }
                    }
                    else//normal accept_fd
                    {
                        if(FD_ISSET(fds[i],&rset))
                        {
                            char buf[1024];
                            memset(buf,'\0',sizeof(buf));
                            ssize_t _s = read(fds[i],buf,sizeof(buf)-1);
                            if(_s > 0)
                            {
                                buf[_s-1] = '\0';
                                printf("client$ %s\n",buf);
                            }
                            else if(_s == 0)
                            {
                                printf("%d is read done......\n",fds[i]);
                                write(fds[i],"mn",2);
                                sleep(10);
                                close(fds[i]);
                                fds[i] = -1;
                            }
                            else
                            {
                                perror("read()");
                            }
                        }

                    }
                }
                break;
        }
    }
    return 0;
}
运行结果:





0 0
原创粉丝点击