高级I/O之多路转接select

来源:互联网 发布:用centos开发安卓软件 编辑:程序博客网 时间:2024/05/16 12:28

一.关于I/O

要提高网络服务服务器,提高I/O性能,本质上是在提高“等”的比重,“等”的比重趋于零,性能越好,而I/O中为了减少等的比重,可以让I/O一次等多个文件描述符,即I/O模型中的多路复用模型,本文则讨论的是多路复用之select模型

二.select函数

1.select函数的作用

系统提供select函数来实现多路复用输入/输出模型。select系统调用是用来让我们的程序监视多个文件句柄的状态变化。程序会在select那里以timeout的形式等待,直到被监视的文件句柄有一个或者多个发生了改变。(关于文件句柄其实就是一个整数,我们最熟悉的句柄是0,1,2三个0是标准输入流,1是标准输出流,2是标准错误输出。0,1,2对应的FILE*结构的表示就是stdin,stdout,stderr。)
这里写图片描述

2.select函数的参数

  • 参数nfds就是需要监视的最大文件描述符值+1
  • 参数rdset,wrset,exset分别对应于需要检测的可读文件描述符的集合,可写文件描述符的集合和异常文件描述符的集合。
  • 参数timeout为结构体timeval,用来设置select()的等待时

    • timeout为NULL ,则表示select将一直被阻塞,直到某个文件描述符上发生了事件。
    • 为0,则表示仅仅检测文件描述符集合的状态,然后立即返回,并不等待外部事件发生
    • 为自己设定的特定的时间值,表示如果在自己指定的时间内没有事件发生,select将超时返回。
  • 函数返回值

    • 执行成功则返回 文件描述词状态已经改变的个数。
    • 如果返回0则表示在文件描述词改变之前已经超过timeout的时间,没有返回。
    • 返回-1则表示有错误发生。

三.自己实现myselect服务器

1.所用到的函数及宏

  • FD_CLR(int fd,fd _set* set):用来清除描述词组set中相关fd的位。
  • FD_ISSET(int fd,fd _set* set):用来测试描述词组set的相关fd的位是否为真
  • FD_SET(int fd,fd _set* set):用来设置描述词组set的相关fd的位
  • FD_ZERO(fd _set* set):用来清除描述词组set的全部位

select服务器端(支持读和写)

#include<stdio.h>#include <sys/types.h>#include <sys/socket.h>#include <sys/time.h>#include <unistd.h>#include<arpa/inet.h>#include<netinet/in.h>#include<string.h>int fds_array[sizeof(fd_set)*8];//用数组的元素来标记对应的位的文件描述符是否有事件发生  有则置1 static void* Usage(const char* proc){    printf("Usage:%s[local_ip][local_port]\n",proc);}int starup(const char* ip,int port){    int  sock = socket(AF_INET,SOCK_STREAM,0);    if(sock < 0)    {        perror("socket");        return 1;    }    struct sockaddr_in local;    local.sin_family = AF_INET;    local.sin_port = htons(port);    local.sin_addr.s_addr = inet_addr(ip);    int b=bind(sock,(struct sockaddr*)&local,sizeof(local));    if(b<0)    {        perror("bind");        return 2;    }    if(listen(sock,10)<0)    {        perror("listen");        return 3;    }    return sock;}int main(int argc, char* argv[]){    if(argc != 3)    {        Usage(argv[0]);        return 4;    }    int listen_sock = starup(argv[1],atoi(argv[2]));    fd_set rfds;    fd_set wfds;    FD_ZERO(&rfds);    FD_ZERO(&wfds);    int i=0;    int num = sizeof(fds_array)/sizeof(fds_array[0]);    for(; i<num; ++i)//给数组的所有标志位均置为-1    {        fds_array[i] = -1;    }    fds_array[0] = listen_sock;    while(1)    {        int maxfd = -1;        for(i=0; i<num; i++)        {            if(fds_array[i]==-1)            {                continue;            }            FD_SET(fds_array[i],&rfds);            FD_SET(fds_array[i],&wfds);            if(maxfd<fds_array[i])            {                maxfd = fds_array[i];            }        }        switch(select(maxfd+1,&rfds,&wfds,NULL,NULL))        {            case 0:                printf("timeout...\n");                break;            case -1:                perror("select");                break;            default:                {   ///at least one read event ready                    for(i=0; i<num; i++)                    {                        struct sockaddr_in client;                        socklen_t len = sizeof(client);                        if(fds_array[i]<0)                        {                            continue;                        }                        if(i==0 && FD_ISSET(listen_sock,&rfds))//listen_sock文件描述符有事件发生                        {                            int new_sock = accept(listen_sock,\                                    (struct sockaddr*)&client,&len);                            if(new_sock<0)                            {                                perror("accept");                                continue;                            }                            printf("get a client [%s:%d]\n",inet_ntoa(client.sin_addr),ntohs(client.sin_port));                            int j=0;                            for(;j<num; j++)                            {                                if(fds_array[j]<0)                                {                                    break;                                }                            }                            if(j==num)                            {                                printf("fd_set full\n");                                close(new_sock);                            }                            else                            {                                fds_array[j] = new_sock;                            }                        }                        else if(i!=0 &&( FD_ISSET(fds_array[i],&rfds)||(FD_ISSET(fds_array[i],&wfds))))//普通文件描述符有事件发生                        {                            char buf[1024];                            if(FD_ISSET(fds_array[i],&rfds))//有读的事件发生                            {                            //  char buf[1024];                                ssize_t s= read(fds_array[i],buf,sizeof(buf)-1);                                if(s>0)                                {                                    buf[s]=0;                                    printf("client#%s\n",buf);                                    if(FD_ISSET(fds_array[i],&wfds))//有写的事件发生                                    {                                        printf("please enter:");                                        fflush(stdout);                                        ssize_t r = read(0,buf,sizeof(buf)-1);                                        if(r>0)                                        {                                            buf[r] = 0;                                            ssize_t w = write(fds_array[i],buf,strlen(buf));                                            if(w>0)                                            {                                                buf[w] = 0;                                                printf("server echo:%s\n",buf);                                            }                                            else if(w==0)                                            {                                                printf("no data enter\n");                                                close(fds_array[i]);                                                fds_array[i] = -1;                                            }                                            else                                            {                                                printf("server error\n");                                                close(fds_array[i]);                                                fds_array[i] = -1;                                            }                                        }                                    }                                }                                else if(s==0)                                {                                    printf("client is quit\n");                                    close(fds_array[i]);                                    fds_array[i] = -1;                                }                                else                                {                                    printf("client error\n");                                    close(fds_array[i]);                                    fds_array[i] = -1;                                }                            }                        }                        else{                        }                    }                }            break;        }    }}

运行结果图:
服务器端:
这里写图片描述
客户端:
这里写图片描述

客户端(普通版本)

#include<stdio.h>#include<stdlib.h>#include<sys/socket.h>#include <netinet/in.h>#include<sys/types.h>#include<arpa/inet.h>#include<string.h>#include<fcntl.h>#include<unistd.h>static void* Usage(const char* proc){    printf("Usage:%s[local_ip][local_port]\n",proc);}int main(int argc,char* argv[]){    if(argc != 3)    {        Usage(argv[0]);        return 1;    }    int sock = socket(AF_INET,SOCK_STREAM,0);    if(sock < 0 )    {        perror("socket");        exit(2);    }    struct sockaddr_in server_sock;    server_sock.sin_family = AF_INET;    server_sock.sin_port = htons(atoi(argv[2]));    server_sock.sin_addr.s_addr = inet_addr(argv[1]);    if(connect(sock,(struct sockaddr*)&server_sock,sizeof(server_sock))<0)    {        printf("connect failed ");        exit(3);    }    printf("connect success\n");    char  buf[1024];    while(1)    {        printf("Please Enter#");        fflush(stdout);        ssize_t s=read(0,buf,sizeof(buf)-1);        if(s>0)        {            buf[s-1]=0;            write(sock,buf,strlen(buf));            ssize_t s = read(sock,buf,sizeof(buf)-1);            if(s>0)            {                buf[s] = 0;                printf("server ehco:%s\n ",buf);            }        }    }    return 0;}

结果图:
服务器端:
这里写图片描述
客户端
这里写图片描述