国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 服務器 > Web服務器 > 正文

IO多路復用之poll全面總結(必看篇)

2024-09-01 13:49:49
字體:
來源:轉載
供稿:網友

1、基本知識

poll的機制與select類似,與select在本質上沒有多大差別,管理多個描述符也是進行輪詢,根據描述符的狀態進行處理,但是poll沒有最大文件描述符數量的限制。poll和select同樣存在一個缺點就是,包含大量文件描述符的數組被整體復制于用戶態和內核的地址空間之間,而不論這些文件描述符是否就緒,它的開銷隨著文件描述符數量的增加而線性增大。

2、poll函數

函數格式如下所示:

# include <poll.h>
int poll ( struct pollfd * fds, unsigned int nfds, int timeout);

pollfd結構體定義如下:

struct pollfd {int fd;     /* 文件描述符 */short events;     /* 等待的事件 */short revents;    /* 實際發生了的事件 */} ; 

 

每一個pollfd結構體指定了一個被監視的文件描述符,可以傳遞多個結構體,指示poll()監視多個文件描述符。每個結構體的events域是監視該文件描述符的事件掩碼,由用戶來設置這個域。revents域是文件描述符的操作結果事件掩碼,內核在調用返回時設置這個域。events域中請求的任何事件都可能在revents域中返回。合法的事件如下:

POLLIN 有數據可讀。

POLLRDNORM   有普通數據可讀。

POLLRDBAND  有優先數據可讀。

POLLPRI 有緊迫數據可讀。

POLLOUT      寫數據不會導致阻塞。

POLLWRNORM   寫普通數據不會導致阻塞。

POLLWRBAND    寫優先數據不會導致阻塞。

POLLMSGSIGPOLL 消息可用。

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

POLLER   指定的文件描述符發生錯誤。

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

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

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

使用poll()和select()不一樣,你不需要顯式地請求異常情況報告。

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

timeout參數指定等待的毫秒數,無論I/O是否準備好,poll都會返回。timeout指定為負數值表示無限超時,使poll()一直掛起直到一個指定事件發生;timeout為0指示poll調用立即返回并列出準備好I/O的文件描述符,但并不等待其它的事件。這種情況下,poll()就像它的名字那樣,一旦選舉出來,立即返回。

返回值和錯誤代碼

成功時,poll()返回結構體中revents域不為0的文件描述符個數;如果在超時前沒有任何事件發生,poll()返回0;失敗時,poll()返回-1,并設置errno為下列值之一:
EBADF       一個或多個結構體中指定的文件描述符無效。

EFAULTfds 指針指向的地址超出進程的地址空間。

EINTR  請求的事件之前產生一個信號,調用可以重新發起。

EINVALnfds參數超出PLIMIT_NOFILE值。

ENOMEM     可用內存不足,無法完成請求。

3、測出程序

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

服務器端程序如下:

#include <stdio.h>#include <stdlib.h>#include <string.h>#include <errno.h>#include <netinet/in.h>#include <sys/socket.h>#include <poll.h>#include <unistd.h>#include <sys/types.h>#define IPADDRESS  "127.0.0.1"#define PORT    8787#define MAXLINE   1024#define LISTENQ   5#define OPEN_MAX  1000#define INFTIM   -1//函數聲明//創建套接字并進行綁定static int socket_bind(const char* ip,int port);//IO多路復用pollstatic void do_poll(int listenfd);//處理多個連接static void handle_connection(struct pollfd *connfds,int num);int main(int argc,char *argv[]){  int listenfd,connfd,sockfd;  struct sockaddr_in cliaddr;  socklen_t cliaddrlen;  listenfd = socket_bind(IPADDRESS,PORT);  listen(listenfd,LISTENQ);  do_poll(listenfd);  return 0;}static int socket_bind(const char* ip,int port){  int listenfd;  struct sockaddr_in servaddr;  listenfd = socket(AF_INET,SOCK_STREAM,0);  if (listenfd == -1)  {    perror("socket error:");    exit(1);  }  bzero(&servaddr,sizeof(servaddr));  servaddr.sin_family = AF_INET;  inet_pton(AF_INET,ip,&servaddr.sin_addr);  servaddr.sin_port = htons(port);  if (bind(listenfd,(struct sockaddr*)&servaddr,sizeof(servaddr)) == -1)  {    perror("bind error: ");    exit(1);  }  return listenfd;}static void do_poll(int listenfd){  int connfd,sockfd;  struct sockaddr_in cliaddr;  socklen_t cliaddrlen;  struct pollfd clientfds[OPEN_MAX];  int maxi;  int i;  int nready;  //添加監聽描述符  clientfds[0].fd = listenfd;  clientfds[0].events = POLLIN;  //初始化客戶連接描述符  for (i = 1;i < OPEN_MAX;i++)    clientfds[i].fd = -1;  maxi = 0;  //循環處理  for ( ; ; )  {    //獲取可用描述符的個數    nready = poll(clientfds,maxi+1,INFTIM);    if (nready == -1)    {      perror("poll error:");      exit(1);    }    //測試監聽描述符是否準備好    if (clientfds[0].revents & POLLIN)    {      cliaddrlen = sizeof(cliaddr);      //接受新的連接      if ((connfd = accept(listenfd,(struct sockaddr*)&cliaddr,&cliaddrlen)) == -1)      {        if (errno == EINTR)          continue;        else        {          perror("accept error:");          exit(1);        }      }      fprintf(stdout,"accept a new client: %s:%d/n", inet_ntoa(cliaddr.sin_addr),cliaddr.sin_port);      //將新的連接描述符添加到數組中      for (i = 1;i < OPEN_MAX;i++)      {        if (clientfds[i].fd < 0)        {          clientfds[i].fd = connfd;          break;        }      }      if (i == OPEN_MAX)      {        fprintf(stderr,"too many clients./n");        exit(1);      }      //將新的描述符添加到讀描述符集合中      clientfds[i].events = POLLIN;      //記錄客戶連接套接字的個數      maxi = (i > maxi ? i : maxi);      if (--nready <= 0)        continue;    }    //處理客戶連接    handle_connection(clientfds,maxi);  }}static void handle_connection(struct pollfd *connfds,int num){  int i,n;  char buf[MAXLINE];  memset(buf,0,MAXLINE);  for (i = 1;i <= num;i++)  {    if (connfds[i].fd < 0)      continue;    //測試客戶描述符是否準備好    if (connfds[i].revents & POLLIN)    {      //接收客戶端發送的信息      n = read(connfds[i].fd,buf,MAXLINE);      if (n == 0)      {        close(connfds[i].fd);        connfds[i].fd = -1;        continue;      }      // printf("read msg is: ");      write(STDOUT_FILENO,buf,n);      //向客戶端發送buf      write(connfds[i].fd,buf,n);    }  }}

客戶端代碼如下所示:

#include <netinet/in.h>#include <sys/socket.h>#include <stdio.h>#include <string.h>#include <stdlib.h>#include <poll.h>#include <time.h>#include <unistd.h>#include <sys/types.h>#define MAXLINE   1024#define IPADDRESS  "127.0.0.1"#define SERV_PORT  8787#define max(a,b) (a > b) ? a : bstatic void handle_connection(int sockfd);int main(int argc,char *argv[]){  int         sockfd;  struct sockaddr_in servaddr;  sockfd = socket(AF_INET,SOCK_STREAM,0);  bzero(&servaddr,sizeof(servaddr));  servaddr.sin_family = AF_INET;  servaddr.sin_port = htons(SERV_PORT);  inet_pton(AF_INET,IPADDRESS,&servaddr.sin_addr);  connect(sockfd,(struct sockaddr*)&servaddr,sizeof(servaddr));  //處理連接描述符  handle_connection(sockfd);  return 0;}static void handle_connection(int sockfd){  char  sendline[MAXLINE],recvline[MAXLINE];  int   maxfdp,stdineof;  struct pollfd pfds[2];  int n;  //添加連接描述符  pfds[0].fd = sockfd;  pfds[0].events = POLLIN;  //添加標準輸入描述符  pfds[1].fd = STDIN_FILENO;  pfds[1].events = POLLIN;  for (; ;)  {    poll(pfds,2,-1);    if (pfds[0].revents & POLLIN)    {      n = read(sockfd,recvline,MAXLINE);      if (n == 0)      {          fprintf(stderr,"client: server is closed./n");          close(sockfd);      }      write(STDOUT_FILENO,recvline,n);    }    //測試標準輸入是否準備好    if (pfds[1].revents & POLLIN)    {      n = read(STDIN_FILENO,sendline,MAXLINE);      if (n == 0)      {        shutdown(sockfd,SHUT_WR);    continue;      }      write(sockfd,sendline,n);    }  }}

4、程序測試結果

linux,io多路復用

linux,io多路復用

linux,io多路復用

以上就是小編為大家帶來的IO多路復用之poll全面總結(必看篇)全部內容了,希望大家多多支持VEVB武林網~


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 平塘县| 罗山县| 广平县| 平邑县| 霸州市| 漯河市| 贵阳市| 金寨县| 泽库县| 桦甸市| 遂溪县| 德兴市| 靖西县| 江油市| 莎车县| 达孜县| 阳春市| 射洪县| 昌黎县| 大方县| 阜宁县| 崇礼县| 伊通| 玉山县| 沙坪坝区| 凯里市| 宽城| 宁城县| 砀山县| 京山县| 霍城县| 邢台市| 盐池县| 黄龙县| 敦煌市| 留坝县| 灵武市| 苏尼特右旗| 永新县| 大方县| 大方县|