IO多路復(fù)用之poll-創(chuàng)新互聯(lián)

poll提供的功能與select類似,與select在本質(zhì)上沒有多大差別,管理多個描述符也是進行輪詢,但poll比select的優(yōu)點是,不限制所能監(jiān)視的描述符的數(shù)目,但隨著所監(jiān)視描述符的數(shù)目的增加,性能也會下降

成都創(chuàng)新互聯(lián)公司制作網(wǎng)站網(wǎng)頁找三站合一網(wǎng)站制作公司,專注于網(wǎng)頁設(shè)計,網(wǎng)站制作、成都網(wǎng)站建設(shè),網(wǎng)站設(shè)計,企業(yè)網(wǎng)站搭建,網(wǎng)站開發(fā),建網(wǎng)站業(yè)務(wù),680元做網(wǎng)站,已為成百上千服務(wù),成都創(chuàng)新互聯(lián)公司網(wǎng)站建設(shè)將一如既往的為我們的客戶提供最優(yōu)質(zhì)的網(wǎng)站建設(shè)、網(wǎng)絡(luò)營銷推廣服務(wù)!

函數(shù)原型:

#include <poll.h>

int poll(struct pollfd *fds, nfds_t nfds, int timeout);

返回值:成功時,poll()返回結(jié)構(gòu)體中revents域不為0的文件描述符個數(shù);如果在超時前沒有任何事件發(fā)生,返回0;失敗返回-1

參數(shù):

fds:結(jié)構(gòu)體指針,該結(jié)構(gòu)體結(jié)構(gòu)如下:

        struct pollfd{

              int fd;        //所感興趣的文件描述符

              short events;   //用于指定等待的事件

              short revents;  //用于指定poll返回時,在該文件描述符上實際發(fā)生了的事件

          };

每一個pollfd結(jié)構(gòu)體制定了一個被監(jiān)視文件描述符,可傳遞多個該結(jié)構(gòu)體,指示poll監(jiān)視多個文件描述符

nfds:要監(jiān)視的描述符的個數(shù)

timeout:單位(微秒),timeout指定等待的毫秒數(shù),無論I/O是否準(zhǔn)備好,poll都會返回,指定為負(fù)數(shù)值表示無限超時,使poll()一直掛起直到一個指定事件發(fā)生;timeout為0指示poll調(diào)用立即返回并列出準(zhǔn)備好I/O的文件描述符,但并不等待其它的事件,立即返回

events域中請求的任何事件都可能在revents域中返回。合法的事件如下:

   POLLIN         有數(shù)據(jù)可讀

   POLLPRI        有緊迫數(shù)據(jù)可讀

POLLOUT         寫數(shù)據(jù)不會導(dǎo)致阻塞

POLLRDNORM       有普通數(shù)據(jù)可讀。

POLLRDBAND      有優(yōu)先數(shù)據(jù)可讀。

POLLWRNORM     寫普通數(shù)據(jù)不會導(dǎo)致阻塞。

POLLWRBAND      寫優(yōu)先數(shù)據(jù)不會導(dǎo)致阻塞。

POLLMSGSIGPOLL    消息可用。

此外,revents域中還可能返回下列事件:
POLLER        指定的文件描述符發(fā)生錯誤。

POLLHUP       指定的文件描述符掛起事件。

POLLNVAL      指定的文件描述符非法。

    這些事件在events域中無意義,因為它們在合適的時候總是會從revents中返回。

POLLIN | POLLPRI等價于select()的讀事件,POLLOUT |POLLWRBAND等價于select()的寫事件。POLLIN等價于POLLRDNORM |POLLRDBAND,而POLLOUT則等價于POLLWRNORM。例如,要同時監(jiān)視一個文件描述符是否可讀和可寫,我們可以設(shè)置 events為POLLIN |POLLOUT。在poll返回時,我們可以檢查revents中的標(biāo)志,對應(yīng)于文件描述符請求的events結(jié)構(gòu)體。如果POLLIN事件被設(shè)置,則文件描述符可以被讀取而不阻塞。如果POLLOUT被設(shè)置,則文件描述符可以寫入而不導(dǎo)致阻塞。這些標(biāo)志并不是互斥的:它們可能被同時設(shè)置,表示這個文件描述符的讀取和寫入操作都會正常返回而不阻塞。

示例代碼如下:

編寫一個echo server程序,功能是客戶端向服務(wù)器發(fā)送信息,服務(wù)器接收輸出并原樣發(fā)送回給客戶端,客戶端接收到輸出到終端

server_poll.c

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

#define _BLOCKLOG_ 6

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

int create(char *_ip,int _port)
{
    int listen_fd=socket(AF_INET,SOCK_STREAM,0);
    if(listen_fd<0){
        perror("socket");
        exit(1);
    }   
    
    struct sockaddr_in local;
    local.sin_family=AF_INET;
    local.sin_port=htons(_port);
    local.sin_addr.s_addr=inet_addr(_ip);

    struct linger lig;
    int iLen;
    lig.l_onoff=1;
    lig.l_linger=0;
    iLen=sizeof(struct linger);
    setsockopt(listen_fd,SOL_SOCKET,SO_LINGER,(char *)&lig,iLen);

    if(bind(listen_fd,(struct sockaddr*)&local,sizeof(local))<0){
        perror("bind");
        exit(2);
    }

    if(listen(listen_fd,_BLOCKLOG_)<0){
        perror("listen");
        exit(3);
    }

    return listen_fd;
}

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

    char *ip=argv[1];
    int port=atoi(argv[2]);
    //創(chuàng)建監(jiān)聽描述符并綁定
    int listen_fd=create(ip,port);

    nfds_t nfds=64;
    struct pollfd fds[nfds];

    //初始化描述符
    int i=0;
    for(;i<nfds;++i){
        fds[i].fd=-1;
    }  
    
    //添加監(jiān)聽描述符
    fds[0].fd=listen_fd;
    fds[0].events=POLLIN;

    struct sockaddr_in client;
    socklen_t len;

    char buf[1024];
    int maxi=0;

    int done=0;
    while(!done){
        int timeout=5000;
        switch(poll(fds,maxi+1,timeout)){
            case -1:
                perror("poll");
                break;
            case 0:
                printf("poll timeout...\n");
                break;
            default:
                {
                    int i=0;
                    for(;i<nfds;++i){
                        if(fds[i].fd==listen_fd && (fds[i].revents&POLLIN)){//listen event happend
                            int new_socket=accept(listen_fd,(struct sockaddr*)&client,&len);
                            if(new_socket<0){
                                perror("accept");
                                continue;
                            }else{
                                printf("get a connection...%s:%d\n",inet_ntoa(client.sin_addr),ntohs(client.sin_port));
                                int i=0;
                                //將新的連接和讀事件添加到數(shù)組中
                                for(;i<nfds;++i){
                                    if(fds[i].fd<0){
                                        fds[i].fd=new_socket;
                                        fds[i].events=POLLIN;
                                        ++maxi;//更新客戶連接的文件描述符的個數(shù)
                                        break;
                                    }
                                }
                            }
                        }else if(fds[i].fd>0 && (fds[i].revents&POLLIN)){//normal events happend
                                //接收客戶端發(fā)來的消息
                            ssize_t _size=read(fds[i].fd,buf,sizeof(buf)-1);
                            if(_size<0){
                                perror("read");
                            }else if(_size==0){//client closed
                                printf("client shutdown\n");
                                close(fds[i].fd);
                                fds[i].fd=-1;
                                continue;
                            }else{
                                buf[_size]='\0';
                                printf("client# %s",buf);
                                //向客戶端發(fā)送消息
                                if(write(fds[i].fd,buf,sizeof(buf)-1)<0){
                                    perror("write");
                                }
                            }
                        }
                    }
                    break;
                }
                break;
        }
    }

    return 0;
}

client_poll.c

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

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

int creat_socket()
{
    int fd=socket(AF_INET,SOCK_STREAM,0);
    if(fd<0){
        perror("socket");
        exit(1);
    }

    return fd;
}

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

    int fd=creat_socket();

    int _port=atoi(argv[2]);
    struct sockaddr_in addr;
    addr.sin_family=AF_INET;
    addr.sin_port=htons(_port);
    inet_aton(argv[1],&addr.sin_addr);
    socklen_t addrlen=sizeof(addr);
    if(connect(fd,(struct sockaddr*)&addr,addrlen)<0){
        perror("connect");
        exit(2);
    }

    char buf[1024];
    while(1){
        memset(buf,'\0',sizeof(buf));
        printf("Please Enter:");
        fgets(buf,sizeof(buf)-1,stdin);
        if(send(fd,buf,sizeof(buf)-1,0)<0){
            perror("send");
            continue;
        }

        ssize_t _size=recv(fd,buf,sizeof(buf)-1,0);
        if(_size>0){
            buf[_size]='\0';
            printf("echo->%s",buf);
        }
  }
  return 0;
}

運行結(jié)果:

服務(wù)器端運行結(jié)果:

IO多路復(fù)用之poll

客戶端1運行結(jié)果:

IO多路復(fù)用之poll

客戶端2運行結(jié)果:

IO多路復(fù)用之poll

最后還有一點,poll()會自動把revents域設(shè)置為0,不需要我們手動的去設(shè)置

測試程序如下:

以下程序片段只是在上面代碼的基礎(chǔ)上添加了幾行代碼,為看的更清楚,我用紅色注釋了這幾行代碼

IO多路復(fù)用之poll

運行出來的結(jié)果如下:

IO多路復(fù)用之poll

現(xiàn)在對上面運行結(jié)果進行分析,為簡單起見,我們只關(guān)注四個結(jié)構(gòu)體,所以以四行為單位

可看到第一次(前四行)的revents都為隨機值

第二次(次四行)poll()函數(shù)把fd=3的描述符中revents設(shè)置為0

第四次poll()函數(shù)把fd=3的描述符中revents設(shè)置為1(因為此時監(jiān)聽到了客戶端請求)所以接下來有:

get a connection...1.0.0.0:0  這條消息

第五次fd=3的revents還是為1,因為此時還沒有執(zhí)行poll(),fd=4的revents是隨機值

第六次fd=3和fd=4的events都為1,它們都添加了讀事件,而fd=3的revents被poll設(shè)置為了0,fd=4的revents為1(因為客戶端發(fā)送了條消息,使讀事件發(fā)生,因此有下面一條消息:)

client# hello

第七次和第六次的一致,因為還未被poll()設(shè)置

第八次和第七次的一致,但此時fd=4的revents=1是因為客戶端按下了Ctrl+c,所以接下來會有:

client shutdown

第九次把fd=4從數(shù)組中去掉

《完》

另外有需要云服務(wù)器可以了解下創(chuàng)新互聯(lián)scvps.cn,海內(nèi)外云服務(wù)器15元起步,三天無理由+7*72小時售后在線,公司持有idc許可證,提供“云服務(wù)器、裸金屬服務(wù)器、高防服務(wù)器、香港服務(wù)器、美國服務(wù)器、虛擬主機、免備案服務(wù)器”等云主機租用服務(wù)以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡單易用、服務(wù)可用性高、性價比高”等特點與優(yōu)勢,專為企業(yè)上云打造定制,能夠滿足用戶豐富、多元化的應(yīng)用場景需求。

網(wǎng)站名稱:IO多路復(fù)用之poll-創(chuàng)新互聯(lián)
標(biāo)題鏈接:http://bm7419.com/article30/dpdgso.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供App設(shè)計、全網(wǎng)營銷推廣、動態(tài)網(wǎng)站、Google、ChatGPT、服務(wù)器托管

廣告

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

手機網(wǎng)站建設(shè)