本文部分轉載於:
http://blog.csdn.net/wqx521/article/details/53783222
http://blog.csdn.net/orz415678659/article/details/8958415
poll提供的功能與select類似,它和select在本質上沒有多大差別,但是poll沒有最大文件描述符數量的限制,而且在處理流設備時,它能夠提供額外的信息。
poll和select同樣存在一個缺點就是,包含大量文件描述符的數組被整體複製於用戶態和內核的地址空間之間,而不論這些文件描述符是否就緒,它的開銷隨着文件描述符數量的增加而線性增大。
另外,select()和poll()將就緒的文件描述符告訴進程後,如果進程沒有對其進行IO操作,那麼下次調用select()和poll()的時候將再次報告這些文件描述符,所以它們一般不會丟失就緒的消息,這種方式稱爲水平觸發(Level Triggered)。
函數原型如下:
#include <poll.h>
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
struct pollfd {
int fd; /* 文件描述符 */
short events; /* 監控的事件 */
short revents; /* 監控事件中滿足條件返回的事件 */
};
#define POLLIN 0x0001 //不阻塞地可讀高優先級外的數據
#define POLLRDNORM 0x0040 //不阻塞地可讀普通數據
#define POLLRDBAND 0x0080 //不阻塞地可讀非0優先級波段數據
#define POLLPRI 0x0002 //不阻塞地可讀高優先級數據
#define POLLOUT 0x0004 //不阻塞地可寫普通數據
#define POLLWRNORM 0x0100 //不阻塞地可寫普通數據
#define POLLWRBAND 0x0200 //不阻塞地可寫非0優先級波段數據
#define POLLERR 0x0008 //已出錯
#define POLLHUP 0x0010 //已掛斷
#define POLLNVAL 0x0020 //描述符不引用-打開文件
nfds 監控數組中有多少文件描述符需要被監控
timeout
-1 永遠等待
0 不等待
>0 等待時間
C/S模型舉例:
注:其中wrap.h和wrap.c請查看第五十篇博客
/* server.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <poll.h>
#include <errno.h>
#include "wrap.h"
#define MAXLINE 80
#define SERV_PORT 8000
#define OPEN_MAX 1024
int main(int argc, char *argv[])
{
int i, j, maxi, listenfd, connfd, sockfd;
int nready;
ssize_t n;
char buf[MAXLINE], str[INET_ADDRSTRLEN];
socklen_t clilen;
struct pollfd client[OPEN_MAX];
struct sockaddr_in cliaddr, servaddr;
listenfd = Socket(AF_INET, SOCK_STREAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(SERV_PORT);
Bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
Listen(listenfd, 20);
client[0].fd = listenfd;
client[0].events = POLLRDNORM; /* listenfd監聽普通讀事件*/
for (i = 1; i < OPEN_MAX; i++)
client[i].fd = -1; /* 用-1初始化client[]裏剩下元素*/
maxi = 0; /* client[]數組有效元素中最大元素下標*/
while(1)
{
nready = poll(client, maxi+1, -1); /* 阻塞*/
if (client[0].revents & POLLRDNORM)
{ /* 有客戶端鏈接請求*/
clilen = sizeof(cliaddr);
connfd = Accept(listenfd, (struct sockaddr *)&cliaddr, &clilen);
printf("received from %s at PORT %d\n",
inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)),
ntohs(cliaddr.sin_port));
for (i = 1; i < OPEN_MAX; i++)
if (client[i].fd < 0)
{
/* 找到client[]中空閒的位置,存放accept返回的connfd */
client[i].fd = connfd;
break;
}
if (i == OPEN_MAX)
perr_exit("too many clients");
/* 設置剛剛返回的connfd,監控讀事件*/
client[i].events = POLLRDNORM;
if (i > maxi)
maxi = i; /* 更新client[]中最大元素下標*/
if (--nready <= 0)
continue; /* 沒有更多就緒事件時,繼續回到poll阻塞*/
}
for (i = 1; i <= maxi; i++)
{ /* 檢測client[] */
if ( (sockfd = client[i].fd) < 0)
continue;//不是響應的客戶端就跳過
if (client[i].revents & (POLLRDNORM | POLLERR))
{//是讀事件
if ( (n = Read(sockfd, buf, MAXLINE)) < 0)
{
if (errno == ECONNRESET)
{ /* 當收到RST標誌時*/
/* connection reset by client */
printf("client[%d] aborted connection\n", i);
Close(sockfd);
client[i].fd = -1;
} else
perr_exit("read error");
} else if (n == 0)
{
/* connection closed by client */
printf("client[%d] closed connection\n", i);
Close(sockfd);
client[i].fd = -1;
} else
{
for (j = 0; j < n; j++)
buf[j] = toupper(buf[j]);
Writen(sockfd, buf, n);
}
if (--nready <= 0)
break; /* no more readable descriptors */
}
}
}
return 0;
}
/* client.c */
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
#include "wrap.h"
#define MAXLINE 80
#define SERV_PORT 8000
int main(int argc, char *argv[])
{
struct sockaddr_in servaddr;
char buf[MAXLINE];
int sockfd, n;
sockfd = Socket(AF_INET, SOCK_STREAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr);
servaddr.sin_port = htons(SERV_PORT);
Connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
while (fgets(buf, MAXLINE, stdin) != NULL) {
Write(sockfd, buf, strlen(buf));
n = Read(sockfd, buf, MAXLINE);
if (n == 0)
printf("the other side has been closed.\n");
else
Write(STDOUT_FILENO, buf, n);
}
Close(sockfd);
return 0;
}