溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

selectI實(shí)現(xiàn)I/O復(fù)用

發(fā)布時(shí)間:2020-07-27 19:28:37 來源:網(wǎng)絡(luò) 閱讀:1056 作者:小止1995 欄目:編程語言

系統(tǒng)提供select函數(shù)來實(shí)現(xiàn)多路復(fù)用輸入/輸出模型。select系統(tǒng)調(diào)用是用來讓我們的程序監(jiān)視多個(gè)文件句柄的狀態(tài)變化的。

select:該函數(shù)允許進(jìn)程指示內(nèi)核等待多個(gè)事件中的任何一個(gè)發(fā)生,并只在有一個(gè)或多個(gè)事件發(fā)生或經(jīng)歷一段指定的時(shí)間后才喚醒它。

selectI實(shí)現(xiàn)I/O復(fù)用

參數(shù)含義:

timeout:它告知內(nèi)核等待所制定描述符中的任何一個(gè)就緒可在多長時(shí)間,其timeval結(jié)構(gòu)用于指定這段時(shí)間的秒數(shù)和微妙數(shù)。

struct timeval

{

    long tv_sec;//seconds

    long tv_usec;//microseconds

}

有三種情況:

  1. 永遠(yuǎn)等待下去:僅在有一個(gè)描述符準(zhǔn)備好I/O時(shí)才返回,為此,我們把該參數(shù)設(shè)為NULL.

  2. 等待固定時(shí)間:在有一個(gè)描述符準(zhǔn)備好I/O時(shí)返回,但是不超過由該參數(shù)所指向的timeval結(jié)構(gòu)中指定的秒數(shù)和微妙數(shù)。

  3. 根本不等待:檢查描述符后立即返回,這稱為輪詢,為此該參數(shù)必須指向一個(gè)timeval結(jié)構(gòu),而且其中的定時(shí)器值必須為0;

前兩種情形的等待通常會(huì)被進(jìn)程在等待期間捕獲的信號(hào)中斷,并從信號(hào)處理函數(shù)返回。

注:有的Linux會(huì)在select函數(shù)返回時(shí)修改timeval結(jié)構(gòu),從移植性考慮,我們應(yīng)假設(shè)timeval結(jié)構(gòu)在函數(shù)返回時(shí)未定義,因而每次調(diào)用select前都得對(duì)它進(jìn)行初始化。
中間的三個(gè)參數(shù)readset,writeset和exceptset制定我們要內(nèi)核測試讀,寫,異常條件的描述符。

異常原因:

   1.某個(gè)套接字的帶外數(shù)據(jù)的到達(dá)

   2.某個(gè)已置為分組模式的偽終端存在可從其主端讀取的控制狀態(tài)信息

select使用描述符集,通常是一個(gè)×××數(shù)組,其中每個(gè)整數(shù)中每一位對(duì)應(yīng)一個(gè)描述符

四個(gè)宏:selectI實(shí)現(xiàn)I/O復(fù)用

maxfd:待測試描述符個(gè)數(shù):待測試描述符加1,描述符1,2...到maxfd-1均被測試。

<sys/select.h>中FD_SETSIZE常值是數(shù)據(jù)類型fd_set中描述符總數(shù),其值通常是1024.

select函數(shù)修改三個(gè)描述符集,參數(shù)是值-結(jié)果參數(shù),調(diào)用時(shí),是關(guān)心描述符值。返回時(shí),指示那些描述符已就緒。

描述符中任何與未就緒描述符對(duì)應(yīng)的位返回時(shí)均被清為0,因此每次重調(diào)時(shí),再次把所有描述符集中所關(guān)心的位置1.

返回值:大于0:所有描述符中已就緒的總位數(shù)。

    等于0:超時(shí)

    -1:出錯(cuò)

就緒條件:

selectI實(shí)現(xiàn)I/O復(fù)用

//監(jiān)視輸入輸出 

#include<stdio.h>                                                        
#include<stdlib.h>
#include<errno.h>
#include<sys/time.h>
#include<sys/select.h>
#include<string.h>
int main()
{
    int read_fd=0;
    int write_fd=1;
    fd_set reads;
    fd_set writes;
    FD_ZERO(&reads);
    FD_ZERO(&writes);
    struct timeval _timeout={5,0};
    int ret=-1;
    int max_fd=write_fd;
    char buf[1024];
    while(1)
    {
         FD_SET(read_fd,&reads);
         FD_SET(write_fd,&writes);
         _timeout.tv_sec=5;
         _timeout.tv_usec=0;
         ret=select(max_fd,&reads,&writes,NULL,&_timeout);
         switch(ret)
         {
             case -1://error
                perror("select");
                break;
             case 0://time out
                printf("time is out...\n");
                break;
             default://normal
                {
                    ssize_t _s;
                    if(FD_ISSET(read_fd,&reads))
                    {
                        _s=read(0,buf,sizeof(buf)-1);
                        buf[_s]='\0';
                        if(strncmp(buf,"quit",4)==0)
                        {
                            printf("quit\n");
                            return 1;
                        }
                        printf("echo: %s",buf);
                    }
                    if(FD_ISSET(write_fd,&writes))
                    {
                        strcpy(buf,"hello world");
                        printf("show: %s\n",buf);
                    }
                }
                break;
         }
     }
    return 0;

運(yùn)行截圖:

selectI實(shí)現(xiàn)I/O復(fù)用

實(shí)現(xiàn)TCP通信,處理任意個(gè)客戶的單進(jìn)程,而不是為每一個(gè)客戶派生一個(gè)子進(jìn)程。

  1. 創(chuàng)建監(jiān)聽套接字并初始化:調(diào)用socket,bind,listen,唯一描述符是監(jiān)聽描述符初始化數(shù)據(jù)結(jié)構(gòu)。

  2. 阻塞于select:select等待某個(gè)事件發(fā)生或新客戶連接的建立或是數(shù)據(jù),F(xiàn)IN或RST的到達(dá)。

  3. accept新連接

     如果監(jiān)聽套接字變?yōu)榭勺x,那么已建立一個(gè)新的連接,我們調(diào)用accept并更新相應(yīng)數(shù)據(jù)結(jié)構(gòu)。使用fds數(shù)組中第一個(gè)未用項(xiàng)記錄這個(gè)已連接描述符。

  4. 檢查現(xiàn)有連接

    對(duì)于每個(gè)現(xiàn)有客戶連接,我們要測試其描述符是否在select返回描述符集中,如果是就從該客戶讀取一行文本,并回顯,輸出。如果該客戶關(guān)閉了連接,那么read將返回0,更新數(shù)據(jù)結(jié)構(gòu)。
//server
#include<stdio.h>                                                        
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#include<sys/select.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<sys/time.h>
#include<sys/types.h>
#include<sys/socket.h>
#define _BACKLOG_ 5
#define _SIZE_ 64
void Usage(const char* proc)
{
    printf("%s [ip][port]\n",proc);
}
int Start(const char* _ip,int _port)
{
    if(_ip==NULL)
        return -1;
    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_addr.s_addr=inet_addr(_ip);
    local.sin_port=htons(_port);
    if(bind(sock,(struct sockaddr*)&local,sizeof(local))<0)
    {
        perror("bind");
        return 2;
    }
    if(listen(sock,_BACKLOG_)<0)
    {
        perror("listen");
        return 3;
    }     
    return sock;
}
int main(int argc,char* argv[])
{
    if(argc!=3)
    {
        Usage(argv[0]);
        return 1;
    }
    char* ip=argv[1];
    int port=atoi(argv[2]);
    int listen_sock=Start(ip,port);
    //allsets存儲(chǔ)舊的描述符集
    fd_set reads,allsets;
    int fds[_SIZE_]={0};
    int max_fd=listen_sock;
    FD_ZERO(&reads);
    FD_ZERO(&allsets);
    FD_SET(listen_sock,&allsets);
    fds[0]=listen_sock;
    int i=0;                                            
    for(i=1;i<_SIZE_;++i)//init fds
    {
        fds[i]=-1;
    }
    struct timeval _timeout={5,0};
    struct sockaddr_in remote;
    socklen_t size=sizeof(remote);  
    char buf[1024];
    ssize_t _s;
    while(1)
    {
        reads=allsets;  
        _timeout.tv_sec=5;
        _timeout.tv_usec=0;
        for(i=0;i<_SIZE_;++i)
        {
            if(fds[i]>max_fd)
                max_fd=fds[i];
        }
        switch(select(max_fd+1,&reads,NULL,NULL,&_timeout))   
        {
            case -1://error
                perror("select");
                return 2;
            case 0://timeout
                printf("time is out...\n");
                break;
            default://normal
            {
                //printf("have a one is comming");//test
                for(i=0;i<_SIZE_;++i)
                {
                    if(fds[i]==listen_sock&&FD_ISSET(fds[i],&reads))
                    {

                        int newsock=accept(listen_sock,(struct sockaddr*)&remote,&size);
                        if(newsock<0)
                        {                  
                        perror("accept");
                            continue;
                        }
                        FD_SET(newsock,&allsets);
                        int j;
                        for(j=0;j<_SIZE_;++j)
                        {
                            if(fds[j]==-1)
                            {
                                fds[j]=newsock;
                                break;
                            }
                        }
                        if(j==_SIZE_)//full
                            close(newsock);
                        break;
                    }
                    else if(fds[i]>0&&FD_ISSET(fds[i],&reads))
                    {
                        _s=read(fds[i],buf,sizeof(buf)-1);    
                         if(_s==0)
                        {
                            fds[i]=-1;
                            FD_CLR(fds[i],&allsets);
                            close(fds[i]);
                            break;
                        }
                        buf[_s]='\0';
                        printf("client:%s",buf);
                        write(fds[i],buf,_s);
                    }
                    else
                    {}
                }
            }
            break;
        }
    }
    for(i=0;i<_SIZE_;++i)             
    {
        if(fds[i]>0)
        {
            close(fds[i]);
        }
    }
    return 0;
}                
//client
#include<stdio.h>                                                       
#include<stdlib.h>
#include<errno.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<string.h>
void Usage(const char* proc)
{
    printf("%s [ip][port]",proc);
}
int main(int argc,char* argv[])
{
    if(argc!=3)
    {
        Usage(argv[0]);
        return 1;
    }
    int client_sock=socket(AF_INET,SOCK_STREAM,0);
    if(client_sock<0)
    {
        perror("socket");
        return 1;
    }
    struct sockaddr_in client;
    client.sin_family=AF_INET;
    client.sin_port=htons(atoi(argv[2]));
    client.sin_addr.s_addr=inet_addr(argv[1]);
    char buf[1024];
    ssize_t _s;
    if(connect(client_sock,(struct sockaddr*)&client,sizeof(client))<0)
    {
        perror("connection");
        return 2;
    }
    while(1)
    {
        printf("please enter:\n");
        _s=read(0,buf,sizeof(buf)-1);        
        if(_s>0)
            buf[_s]='\0';
        if(strncmp(buf,"quit",4)==0)
        {
            printf("client is quit\n");
            break;
        }
        write(client_sock,buf,_s);
        _s=read(client_sock,buf,sizeof(buf)-1);
        if(_s>0)
        {
            buf[_s]='\0';
            printf("server->client: %s",buf);
        }
    }
    close(client_sock);

    return 0;
}

selectI實(shí)現(xiàn)I/O復(fù)用

//回顯的

selectI實(shí)現(xiàn)I/O復(fù)用


向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI