I/O多路轉(zhuǎn)接之select

I/O多路轉(zhuǎn)接之select(只負(fù)責(zé)等)

成都創(chuàng)新互聯(lián)公司專(zhuān)業(yè)為企業(yè)提供赤坎網(wǎng)站建設(shè)、赤坎做網(wǎng)站、赤坎網(wǎng)站設(shè)計(jì)、赤坎網(wǎng)站制作等企業(yè)網(wǎng)站建設(shè)、網(wǎng)頁(yè)設(shè)計(jì)與制作、赤坎企業(yè)網(wǎng)站模板建站服務(wù),十余年赤坎做網(wǎng)站經(jīng)驗(yàn),不只是建網(wǎng)站,更提供有價(jià)值的思路和整體網(wǎng)絡(luò)服務(wù)。

系統(tǒng)提供select函數(shù)來(lái)實(shí)現(xiàn)多路復(fù)用輸入/輸出模型。

 I/O多路轉(zhuǎn)接之select

傳向select的參數(shù)告訴內(nèi)核:

1)我們所關(guān)心的文件描述符。

參數(shù)nfds是需要監(jiān)視的最大的文件描述符值+1;

2)對(duì)每個(gè)描述符,我們所關(guān)心的狀態(tài)。

rdset,wrset,exset分別對(duì)應(yīng)于需要檢測(cè)的可讀文件描述符的集合,可寫(xiě)文件描述符的集合及異常文件描述符的集合。

監(jiān)視的文件描述符分為三類(lèi)set,每一種對(duì)應(yīng)等待不同的事件。readfds中列出的文件描述符被監(jiān)視是否有數(shù)據(jù)可供讀?。ㄈ绻x取操作完成則不會(huì) 阻 塞)。writefds中列出的文件描述符則被監(jiān)視是否寫(xiě)入操作完成而不阻塞后,exceptfds中列出的文件描述符則被監(jiān)視是否發(fā)生異常,或者無(wú) 法控制的數(shù)據(jù)是否可用(這些狀態(tài)僅僅應(yīng)用于套接字)。這三類(lèi)set可以是NULL,這種情況下select()不監(jiān)視這一類(lèi)事件。

下面的宏提供了處理這三種描述詞組的方式:

FD_ZERO移除指定set中的所有文件描述符。每一次調(diào)用select()之前都應(yīng)該先調(diào)用它;

FD_CLR則從指定的set中移除一個(gè)文件描述符;

FD_SET添加一個(gè)文件描述符到指定的set中;

FD_ISSET測(cè)試一個(gè)文件描述符是否指定set的一部分。如果文件描述符在set中則返回一個(gè)非0整數(shù),不在則返0,F(xiàn)D_ISSET在調(diào)用select() 返回之后使用,測(cè)試指定的文件描述符是否準(zhǔn)備好相關(guān)動(dòng)作。

3)我們要等待多長(zhǎng)時(shí)間。(我們可以等待無(wú)限長(zhǎng)的時(shí)間,等待固定的一段時(shí)間,或者根本就不等待)

timeout參數(shù)是一個(gè)指向timeval結(jié)構(gòu)體的指針,timeval定義如下:

 I/O多路轉(zhuǎn)接之select

struct timeval結(jié)構(gòu)用于描述一段時(shí)間長(zhǎng)度,如果在這個(gè)時(shí)間內(nèi),需要監(jiān)視的描述符沒(méi)有事件發(fā)生則函數(shù)返回,返回值為0。

從 select函數(shù)返回后,內(nèi)核告訴我們以下信息:

1)已經(jīng)做好準(zhǔn)備的描述符的個(gè)數(shù)。

2)對(duì)于三種條件哪些描述符已經(jīng)做好準(zhǔn)備。(讀,寫(xiě),異常)

執(zhí)成功則返回件描述詞狀態(tài)已改變的個(gè)數(shù);如果返回0代表在描述詞狀態(tài)改變前已超過(guò)timeout時(shí)間,沒(méi)有返回;
當(dāng)有錯(cuò)誤發(fā)時(shí)則返回-1,錯(cuò)誤原因存于errno,此時(shí)參數(shù)readfds,writefds,exceptfds和timeout的值變成不可預(yù)測(cè)。錯(cuò)誤值可能為:EBADF 件描述詞為效的或該件已關(guān)閉 ,EINTR 此調(diào)被信號(hào)所中斷 EINVAL 參數(shù)n 為負(fù)值,ENOMEM 核內(nèi)存不。

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


int fds[64];
const 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("Usage:%s[IP] [PORT]\n",_proc);
}
int main(int argc,char* argv[])
{
    if(argc!=3)
    {
        usage(argv[0]);
        exit(1);
    }
    int i=0;
    for(i=0;i<fds_nums;++i)
    {
        fds[i]=-1;
    }

    int  listen_sock=startup(argv[1],atoi(argv[2]));
    fd_set rset;
    FD_ZERO(&rset);
    FD_SET(listen_sock,&rset);

    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;
            }
        }

        struct timeval timeout={0,0};
        switch(select(max_fd+1,&rset,NULL,NULL,NULL/*&timeout*/))
        {
            case 0:
                printf("timeout..\n");
                break;
            case 1:
                perror("select");
                break;
            default:
                for(i=0;i<fds_nums;++i)
                {
                    if(i=0&& FD_ISSET(listen_sock,&rset))
                    {
                        struct sockaddr_in peer;
                        socklen_t len=sizeof(peer);
                        int new_fd=accept(listen_sock,(struct sockaddr*)&peer,&len);
                        if(new_fd>0)
                        {
                            printf("get a new client:socket->%s:%d\n",inet_ntoa(peer.sin_addr),ntohs(peer.sin_port));
                            int j=0;
                            for(j=0;j<fds_nums;j++)
                            {                            
                                if(fds[i]==-1)
                                {
                                    fds[j]=new_fd;
                                    break;
                                }
                            }
                            if(j==fds_nums)
                            {
                                close(new_fd);
                            }
                        }
                        else
                        {
                            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)
                                {
                                    printf("client#  %s\n",buf);
                                }
                                else if(_s==0)
                                {
                                printf("client close...\n");
                                close(fds[i]);
                                }
                                else
                                {
                                    perror("read");
                                }
                            }
                        }
                    }
                }
                break;
        }
    }
return 0;
}

select優(yōu)點(diǎn):

(1)相較于之前多線(xiàn)程的方法,使用select不用創(chuàng)建線(xiàn)程,更方便

(2)select目前幾乎在所有的平臺(tái)上都支持,其良好跨平臺(tái)支持也是它的一個(gè)優(yōu)點(diǎn) 

select缺點(diǎn):

(1)每次調(diào)用select,都需要把fd集合從用戶(hù)態(tài)拷貝到內(nèi)核態(tài),這個(gè)開(kāi)銷(xiāo)在fd很多時(shí)會(huì)很大 

(2)同時(shí)每次調(diào)用select都需要在內(nèi)核遍歷傳遞進(jìn)來(lái)的所有fd,這個(gè)開(kāi)銷(xiāo)在fd很多時(shí)也很大 

(3)能夠監(jiān)視的文件描述符的數(shù)量存在最大限制,在Linux上一般為1024,因?yàn)樗蕾?lài)于文件系統(tǒng)

(4)select()所維護(hù)的文件描述符的數(shù)據(jù)結(jié)構(gòu),隨著文件描述符數(shù)量的增大,其復(fù)制的開(kāi)銷(xiāo)也線(xiàn)性增長(zhǎng)。

(5)由于網(wǎng)絡(luò)響應(yīng)時(shí)間的延遲使得大量TCP連接處于非活躍狀態(tài),但調(diào)用select()會(huì)對(duì)所有socket進(jìn)行一次線(xiàn)性?huà)呙?,這也會(huì)有一些開(kāi)銷(xiāo)


網(wǎng)站標(biāo)題:I/O多路轉(zhuǎn)接之select
文章網(wǎng)址:http://muchs.cn/article36/gphgsg.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供域名注冊(cè)、軟件開(kāi)發(fā)、小程序開(kāi)發(fā)、電子商務(wù)、微信公眾號(hào)、ChatGPT

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶(hù)投稿、用戶(hù)轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀(guān)點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話(huà):028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

成都定制網(wǎng)站建設(shè)