真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

IO復(fù)用之——poll

一. 關(guān)于poll

成都創(chuàng)新互聯(lián)公司一直在為企業(yè)提供服務(wù),多年的磨煉,使我們?cè)趧?chuàng)意設(shè)計(jì),全網(wǎng)整合營(yíng)銷推廣到技術(shù)研發(fā)擁有了開發(fā)經(jīng)驗(yàn)。我們擅長(zhǎng)傾聽企業(yè)需求,挖掘用戶對(duì)產(chǎn)品需求服務(wù)價(jià)值,為企業(yè)制作有用的創(chuàng)意設(shè)計(jì)體驗(yàn)。核心團(tuán)隊(duì)擁有超過十余年以上行業(yè)經(jīng)驗(yàn),涵蓋創(chuàng)意,策化,開發(fā)等專業(yè)領(lǐng)域,公司涉及領(lǐng)域有基礎(chǔ)互聯(lián)網(wǎng)服務(wù)資陽服務(wù)器托管成都app軟件開發(fā)公司、手機(jī)移動(dòng)建站、網(wǎng)頁(yè)設(shè)計(jì)、網(wǎng)絡(luò)整合營(yíng)銷。

    對(duì)于IO復(fù)用模型,其優(yōu)點(diǎn)無疑是免去了對(duì)一個(gè)個(gè)IO事件就緒的等待,轉(zhuǎn)而代之的是同時(shí)對(duì)多個(gè)IO數(shù)據(jù)的檢測(cè),當(dāng)檢測(cè)等待的事件中至少有一個(gè)就緒的時(shí)候,就會(huì)返回告訴用戶進(jìn)程“已經(jīng)有數(shù)據(jù)準(zhǔn)備好了,快看看是哪個(gè)趕緊處理”,而對(duì)于IO復(fù)用的實(shí)現(xiàn),除了可以用select函數(shù),另外一個(gè)函數(shù)仍然支持這種復(fù)用IO模型,就是poll函數(shù);


二. poll函數(shù)的用法

    雖然同樣是對(duì)多個(gè)IO事件進(jìn)行檢測(cè)等待,但poll和select多少還是有些不同的:

IO復(fù)用之——poll

函數(shù)參數(shù)中,

先來說nfds,這個(gè)是指當(dāng)前需要關(guān)心的文件描述符的個(gè)數(shù);

timeout同樣是設(shè)置超時(shí)時(shí)間,只是和select的timeout是一個(gè)結(jié)構(gòu)體不一樣,這里只是一個(gè)整型類型,且含義是毫秒;

fds是一個(gè)結(jié)構(gòu)體指針,如下:

IO復(fù)用之——poll

結(jié)構(gòu)體中,

fd表示所要關(guān)心的文件描述符;

events表示該文件描述符所關(guān)心的事件,這是一個(gè)輸入型參數(shù),要告訴操作系統(tǒng)這個(gè)文件描述符對(duì)應(yīng)的事件所關(guān)心的操作事件是什么,比如讀或?qū)懀?/p>

revents是一個(gè)輸出型參數(shù),表示當(dāng)poll返回時(shí)告訴用戶什么操作事件是就緒的,比如如果POLLIN是就緒的,那么返回時(shí)revent的值就是POLLIN,告訴用戶fd事件的POLLIN是就緒的;

events和revents的值可以為如下:

IO復(fù)用之——poll這里要說明選項(xiàng)其實(shí)不止這三個(gè),只是這里的討論中這三個(gè)選項(xiàng)是最常用的;

events設(shè)置為POLLIN表示fd所需要讀取數(shù)據(jù),而revents若返回POLLIN則表示data已經(jīng)ready可以讀取了;

同樣,events設(shè)置為POLLOUT表示fd所關(guān)心數(shù)據(jù)的寫入,而revents返回POLLOUT則表示寫事件就緒可以進(jìn)行數(shù)據(jù)的寫入;

至于POLLPRI,后面的解釋是作為緊急選項(xiàng)來設(shè)置的,在TCP協(xié)議報(bào)文中有個(gè)URG的緊急指針是表示先從緊急數(shù)據(jù)的地方開始讀取,這里也是這個(gè)意思;


三. 栗子時(shí)間

    同樣的,這里可以用poll來編寫基于TCP協(xié)議的server端和client端的數(shù)據(jù)通信,和select一樣,避免使用多進(jìn)程和多線程的方式轉(zhuǎn)而使用對(duì)多個(gè)IO接口的等待:

server服務(wù)器端:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define _BACKLOG_ 5//網(wǎng)絡(luò)中連接請(qǐng)求隊(duì)列中的最大等待數(shù)目
#define _NUM_ 10//IO事件結(jié)構(gòu)體數(shù)組大小

void usage(const char *argv)//命令行參數(shù)的差錯(cuò)判斷
{
    printf("%s   [ip]   [port]\n", argv);
    exit(0);
}

static int CreateListenSocket(int ip, int port)//創(chuàng)建監(jiān)聽套接字
{
    int sock = socket(AF_INET, SOCK_STREAM, 0); 
    if(sock < 0)
    {   
        perror("socket");
        exit(1);
    }   

    struct sockaddr_in server;//設(shè)置本地網(wǎng)絡(luò)地址信息
    server.sin_family = AF_INET;
    server.sin_port = htons(port);
    server.sin_addr.s_addr = ip; 

    if(bind(sock, (struct sockaddr*)&server, sizeof(server)) < 0)//綁定
    {   
        perror("bind");
        exit(2);
    }
    
    if(listen(sock, _BACKLOG_) < 0)//監(jiān)聽
    {
        perror("listen");
        exit(3);
    }

    return sock;
}

int main(int argc, char *argv[])
{
    if(argc != 3)
        usage(argv[0]);
    int port = atoi(argv[2]);
    int ip = inet_addr(argv[1]);

    int listen_sock = CreateListenSocket(ip, port);//獲取監(jiān)聽socket
    struct sockaddr_in client;//用于存放client端的網(wǎng)絡(luò)地址信息
    socklen_t client_len = sizeof(client);

    struct pollfd fds[_NUM_];//用一個(gè)結(jié)構(gòu)體數(shù)組來存放各種IO事件
    fds[0].fd = listen_sock;//將監(jiān)聽套接字放入數(shù)組中
    fds[0].events = POLLIN;//將監(jiān)聽事件需要的event設(shè)置為讀取數(shù)據(jù)POLLIN
    fds[0].revents = 0;

    size_t i = 1;
    for(; i < _NUM_; ++i)//初始化結(jié)構(gòu)體數(shù)組中成員
    {
        fds[i].fd = -1;
        fds[i].events = 0;
        fds[i].revents = 0;
    }
    int max_fd = 1;//設(shè)置最大的文件描述符個(gè)數(shù)
    int timeout = 5000;//設(shè)置超時(shí)時(shí)間
    
    while(1)
    {
        switch(poll(fds, max_fd, timeout))
        {
            case -1://出錯(cuò)
                perror("poll");
                break;
            case 0://超時(shí)
                printf("timeout...\n");
                break;
            default://至少有一個(gè)事件已經(jīng)就緒,可以進(jìn)行IO數(shù)據(jù)的操作
                {
                    size_t i = 0;
                    for(; i < _NUM_; ++i)
                    {
                            //判斷是否為監(jiān)聽事件就緒,如果是代表有連接請(qǐng)求需要處理
                        if((fds[i].fd == listen_sock) && (fds[i].revents == POLLIN))
                        {
                                //處理連接
                            int accept_sock = accept(listen_sock, (struct sockaddr*)&client, &client_len);
                            if(accept_sock < 0)
                            {
                                perror("accept");
                                continue;
                            }
                            printf("connect with a client... [ip]:%s  [port]:%d\n", inet_ntoa(client.sin_addr), ntohs(client.sin_port));

                            size_t i = 0;
                            //將創(chuàng)建出新的數(shù)據(jù)傳輸socket文件描述符設(shè)置進(jìn)poll結(jié)構(gòu)體數(shù)組
                            for(; i < _NUM_; ++i)
                            {
                                if(fds[i].fd == -1)
                                {
                                    fds[i].fd = accept_sock;
                                    fds[i].events = POLLIN;//將事件設(shè)置為需要讀取數(shù)據(jù)
                                    max_fd++;//更新最大文件描述符個(gè)數(shù)
                                    break;
                                }
                                
                            }
                            if(i == _NUM_)
                                close(accept_sock);//若超出數(shù)組個(gè)數(shù)則表明當(dāng)前無法處理,關(guān)閉掉
                        }//表示為其他除了listen socket之外的要進(jìn)行數(shù)據(jù)傳輸?shù)膕ocket
                        else if((fds[i].fd > 0) && (fds[i].revents == POLLIN))
                        {
                            char buf[1024];
                            //進(jìn)行數(shù)據(jù)的讀取
                            ssize_t size = read(fds[i].fd, buf, sizeof(buf)-1);
                            if(size < 0)//讀取出錯(cuò)
                                perror("read");
                            else if(size == 0)//客戶端關(guān)閉,將結(jié)構(gòu)體數(shù)組中相應(yīng)位置清空
                            {
                                 printf("client closed...\n");
                                 //將要關(guān)閉的文件描述符和結(jié)構(gòu)體數(shù)組中最后一個(gè)有效的文件描述符進(jìn)行交換
                                 //確保當(dāng)前有效的文件描述符在數(shù)組內(nèi)都是連續(xù)的
                                                                 struct pollfd tmp = fds[i];
                                                                 fds[i] = fds[max_fd-1];
                                                                 fds[max_fd-1] = tmp;
                                                                 
                                                                 //交換后要?jiǎng)h除的就在數(shù)組的最后,關(guān)閉且將數(shù)組內(nèi)相應(yīng)位置為無效值
                                                                 close(fds[max_fd-1].fd);
                                                                 fds[max_fd-1].fd = -1; 
                                                                 fds[max_fd-1].events = 0;
                                                                 fds[max_fd-1].revents = 0;
                                                                 --max_fd;//更新當(dāng)前有效文件描述符的個(gè)數(shù)
                            }
                            else//讀取成功,輸出數(shù)據(jù)
                            {
                                buf[size]  ='\0';
                                printf("client# %s\n", buf);
                            }

                        }
                        else
                        {}
                    }
                }
                break;
        }
    }
    return 0;
}

client客戶端這里就不寫了,和基于TCP協(xié)議的socket編程中的client是差不多的;

運(yùn)行程序:

IO復(fù)用之——poll

這里需要說明的是:

  1. 和select不同,select是用一個(gè)fd_set數(shù)據(jù)類型來存放各個(gè)需要操作的IO文件描述符,這里的poll是用一個(gè)結(jié)構(gòu)體來存放文件描述符和鎖關(guān)心的事件類型,因此,poll并沒有處理文件描述符的上限,但相同的是,每一次poll返回都仍然需要遍歷來獲取事件就緒的位置以此來進(jìn)行相應(yīng)的處理,還是一樣有復(fù)制和系統(tǒng)遍歷帶來的額外開銷,當(dāng)處理事件比較多的時(shí)候仍然是低效的;

  2. 在每一次循環(huán)到select之前都需要將對(duì)應(yīng)事件的fd_set的集合調(diào)用FD_ZERO函數(shù)來進(jìn)行重新的初始化清零,因?yàn)椴皇菚?huì)有文件描述符的新建和關(guān)閉,需要進(jìn)行初始化然后再將事件重新一一設(shè)置進(jìn)相應(yīng)的fd_set里面;而poll并不需要,因?yàn)槭褂靡粋€(gè)結(jié)構(gòu)體數(shù)組來管理相當(dāng)于結(jié)合了select中的設(shè)置數(shù)組存放文件描述符和添加設(shè)置這兩步,每一次進(jìn)行poll的時(shí)候都會(huì)將發(fā)生就緒的事件對(duì)應(yīng)的revents置位,當(dāng)處理完畢就會(huì)被系統(tǒng)自動(dòng)歸為0,并不需要進(jìn)行手動(dòng)初始化清零;

  3. 在結(jié)構(gòu)體數(shù)組還未使用之前,和被剛定義的變量一樣,結(jié)構(gòu)體成員fd、events和revents都是隨機(jī)值,雖然后來使用的時(shí)候都會(huì)被賦對(duì)應(yīng)的有效值,但為了避免判斷時(shí)的二義性問題最好還是在循環(huán)使用前都將其初始化為統(tǒng)一的可識(shí)別的無效值;

《完》


網(wǎng)頁(yè)名稱:IO復(fù)用之——poll
本文地址:http://weahome.cn/article/giedcd.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部