高級I/O之多路轉接select

一.關於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;
}

結果圖:
服務器端:
這裏寫圖片描述
客戶端
這裏寫圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章