天天看點

【轉】IO多路複用之poll

____________________________________________

poll提供的功能與select類似,與select在本質上沒有多大差别,管理多個描述符也是進行輪詢,但poll比select的優點是,不限制所能監視的描述符的數目,但随着所監視描述符的數目的增加,性能也會下降

函數原型:

#include <poll.h>

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

傳回值:成功時,poll()傳回結構體中revents域不為0的檔案描述符個數;如果在逾時前沒有任何事件發生,傳回0;失敗傳回-1

參數:

fds:結構體指針,該結構體結構如下:

        struct pollfd{

              int fd;               //所感興趣的檔案描述符

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

              short revents;   //用于指定poll傳回時,在該檔案描述符上實際發生了的事件

          };

每一個pollfd結構體制定了一個被監視檔案描述符,可傳遞多個該結構體,訓示poll監視多個檔案描述符

nfds:要監視的描述符的個數

timeout:機關(微秒),timeout指定等待的毫秒數,無論I/O是否準備好,poll都會傳回,指定為負數值表示無限逾時,使poll()一直挂起直到一個指定事件發生;timeout為0訓示poll調用立即傳回并列出準備好I/O的檔案描述符,但并不等待其它的事件,立即傳回

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

      POLLIN         有資料可讀

      POLLPRI        有緊迫資料可讀

  POLLOUT           寫資料不會導緻阻塞

  POLLRDNORM       有普通資料可讀。

  POLLRDBAND      有優先資料可讀。

  POLLWRNORM     寫普通資料不會導緻阻塞。

  POLLWRBAND       寫優先資料不會導緻阻塞。

  POLLMSGSIGPOLL    消息可用。

  此外,revents域中還可能傳回下列事件:

  POLLER             指定的檔案描述符發生錯誤。

  POLLHUP           指定的檔案描述符挂起事件。

  POLLNVAL         指定的檔案描述符非法。

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

  POLLIN | POLLPRI等價于select()的讀事件,POLLOUT |POLLWRBAND等價于select()的寫事件。POLLIN等價于POLLRDNORM |POLLRDBAND,而POLLOUT則等價于POLLWRNORM。例如,要同時監視一個檔案描述符是否可讀和可寫,我們可以設定 events為POLLIN |POLLOUT。在poll傳回時,我們可以檢查revents中的标志,對應于檔案描述符請求的events結構體。如果POLLIN事件被設定,則檔案描述符可以被讀取而不阻塞。如果POLLOUT被設定,則檔案描述符可以寫入而不導緻阻塞。這些标志并不是互斥的:它們可能被同時設定,表示這個檔案描述符的讀取和寫入操作都會正常傳回而不阻塞。

示例代碼如下:

編寫一個echo server程式,功能是用戶端向伺服器發送資訊,伺服器接收輸出并原樣發送回給用戶端,用戶端接收到輸出到終端

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]);
//建立監聽描述符并綁定
int listen_fd=create(ip,port);

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

//初始化描述符
int i=