天天看點

linux I/O複用---------poll

poll系統調用和select類似,也是在指定的事件内輪詢一定數量的檔案描述符,以測試其中是否有就緒的檔案描述符,不過poll聰明的地方就是它把事件和檔案描述符綁定了起來(後面大家就會知道這個操作的優點了!!!!!!)

《一》poll的函數原型如下:

#include<poll.h>

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

1>fds是一個pollfd結構體類型的數組,它指定所有我們感興趣的檔案描述符上發生的可讀可寫和異常等事件,

struct  pollfd

{

          Int  fd;    //檔案描述符

          Short  events;  //注冊的事件

          Short  revents;  //實際發生的事件,由核心填充

};

(fd   events  由使用者填充,revents由核心填充)

2>Nfds:被監聽事件集合fds的大小,(fds結構體數組的大小)

3>Timeout:逾時時間,為-1時,poll調用永遠阻塞,直到某個事件發生

Poll成功傳回就緒檔案描述符的個數,失敗傳回-1,

《二》poll的原理圖如下:

linux I/O複用---------poll

《三》poll的代碼:

用戶端:

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

void main()
{
    int sockfd=socket(AF_INET,SOCK_STREAM,0);
    assert(sockfd!=-1);

    struct sockaddr_in ser,cli;
    ser.sin_family=AF_INET;
    ser.sin_port=htons(6000);
    ser.sin_addr.s_addr=inet_addr("127.0.0.1");

    
    int n=connect(sockfd,(struct sockaddr*)&ser,sizeof(ser));
    assert(n!=-1);

    while(1)
    {
        char buff[128]={0};
        printf("input:\n");
        fgets(buff,128,stdin);
        
        if(strncmp(buff,"end",3)==0)
        {
            close(n);
            break;
        }
        send(sockfd,buff,strlen(buff)-1,0);
        memset(buff,0,128);
        recv(sockfd,buff,127,0);
        printf("%s\n",buff);
    }
    close(sockfd);
}
        
           

伺服器:

#define _GNU_SOURCE 1
#include<stdio.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<assert.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<fcntl.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<errno.h>
#include<sys/select.h>
#include<poll.h>
#define MAX 1024
void initfd(struct pollfd *fds)
{
    int i=0;
    for(;i<MAX;i++)
    {
        fds[i].fd=-1;
    }
}
void insertfd(struct pollfd *fds,int fd,short events)
{
    int i=0;
    for(;i<MAX;i++)
    {
        if(fds[i].fd==-1)
        {
            fds[i].fd = fd;
            fds[i].events=events;
            break;
        }
    }

}


void main()
{
    int listenfd=socket(AF_INET,SOCK_STREAM,0);
    assert(listenfd!=-1);
    struct sockaddr_in ser,cli;
    ser.sin_family=AF_INET;
    ser.sin_port=htons(6000);
    ser.sin_addr.s_addr=inet_addr("127.0.0.1");



    int res=bind(listenfd,(struct sockaddr*)&ser,sizeof(ser));
    assert(res!=-1);

    listen(listenfd,5);

    struct pollfd fds[MAX];
    initfd(fds);
    fds[0].fd=listenfd;
    fds[0].events=POLLIN;

    while(1)
    {
         int n=poll(fds,MAX,5000);
         if(n==-1)
         {
             printf("poll error\n");
             exit(0);
         }
         else if(n==0)
         {
             printf("time out\n");
             continue;
         }
         else
         {
             int i=0;
             for(;i<MAX;++i)
             {
                if(fds[i].revents & POLLRDHUP)
                {
                    printf("one client break\n");
                    close(fds[i].fd);
                    fds[i].fd=-1;
                }
                if(fds[i].revents & POLLIN)
                {
                    if(fds[i].fd==listenfd)
                    {
                        int len=sizeof(cli);
                        int c=accept(listenfd,(struct sockaddr*)&cli,&len);
                        if(c<0)
                        {
                            printf("one client link error\n");
                            continue;
                        }
                        insertfd(fds,c,POLLIN|POLLRDHUP);
                    }
                    else
                    {
                        char buff[128]={0};
                        int n=recv(fds[i].fd,buff,127,0);
                        if(n<=0)
                        {
                            printf("not get\n");
                            close(fds[i].fd);
                            fds[i].fd=-1;
                            continue;
                        }
                        printf("%d  %s\n",fds[i].fd,buff);
                        send(fds[i].fd,"ok",2,0);
                    }
                }
             }
         }


    }
}
           

《三》Poll與select的相同點:

  1. 傳回的都是檔案描述符的個數,并沒有傳回哪些檔案描述符就緒,是以還需要輪詢檢測哪些檔案描述符就緒,時間複雜度為O(n);
  2. 都會存在兩次拷貝:  

                           1》調用時将使用者空間的資料拷貝到核心空間

                     2》傳回時将核心空間資料拷貝到使用者空間

   3.兩者都隻能在工作在相對低效的LT模式下

《四》Poll相對select的優點:

  1. poll将使用者關注的事件與核心回報的事件分離,是以每次調用poll之前都不中重新設定,(核心隻修改了revents,并麼有修改events的值)
  2. 将事件用單獨的變量表示,能關注的事件類型增多
  3. 檔案描述符用單獨的變量表示,關注的檔案描述符值增大
  4. 使用者關注的所有檔案描述符都記錄在使用者數組中,同時關注的檔案描述符增多(受程序所能打開的檔案描述符的數量限制)