1.poll模型原理
poll模型是基于select最大檔案描述符限制提出的,跟select一樣,隻是将select使用的三個基于位的檔案描述符改為使用一個數組的形式,對于各種可能的事件進行了一個包裝
#include <sys/poll.h>
int poll (struct pollfd *fds, unsigned int nfds, int timeout);
參數說明:
第一個參數fds為一個pollfd結構數組,用來儲存檔案描述符
第二個參數nfds為pollfd結構體數組+1
第三個參數timeout為poll等待時間
傳回值:
正常傳回值為輪詢檔案描述符結構有事件發送的個數,-1傳回失敗
和select()不一樣,poll()沒有使用低效的三個基于位的檔案描述符set,而是采用了一個單獨的結構體pollfd數組,由fds指針指向這個組。pollfd結構體定義如下:
#include <sys/poll.h>
struct pollfd {
int fd; /* file descriptor */
short events; /* requested events to watch */
short revents; /* returned events witnessed */
};
每一個pollfd結構體指定了一個被監視的檔案描述符,可以傳遞多個結構體,訓示poll()監視多個檔案描述符。每個結構體的events域是監視該檔案描述符的事件掩碼,由使用者來設定這個域。revents域是檔案描述符的操作結果事件掩碼。核心在調用傳回時設定這個域。events域中請求的任何事件都可能在revents域中傳回。合法的事件如下:
POLLIN 有資料可讀。
POLLRDNORM 有普通資料可讀。
POLLRDBAND 有優先資料可讀。
POLLPRI 有緊迫資料可讀。
POLLOUT 寫資料不會導緻阻塞。
POLLWRNORM 寫普通資料不會導緻阻塞。
POLLWRBAND 寫優先資料不會導緻阻塞。
POLLMSG SIGPOLL消息可用。
此外,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指定為負數值表示無限逾時;timeout為0訓示poll調用立即傳回并列出準備好I/O的檔案描述符,但并不等待其它的事件。這種情況下,poll()就像它的名字那樣,一旦選舉出來,立即傳回。
傳回值和錯誤代碼
成功時,poll()傳回結構體中revents域不為0的檔案描述符個數;如果在逾時前沒有任何事件發生,poll()傳回0;失敗時,poll()傳回-1,并設定errno為下列值之一:
EBADF 一個或多個結構體中指定的檔案描述符無效。
EFAULT fds指針指向的位址超出程序的位址空間。
EINTR 請求的事件之前産生一個信号,調用可以重新發起。
EINVAL nfds參數超出PLIMIT_NOFILE值。
ENOMEM 可用記憶體不足,無法完成請求。
2.poll工作流程
1.定義一個用戶端連接配接數組
struct pollfd clients[OPEN_MAX];
2.調用poll輪詢檔案描述符事件
nready = poll(clients, maxi + 1, -1);
3.使用執行個體
引用一個簡單的poll伺服器例子
#include <unistd.h>
#include <sys/types.h> /* basic system data types */
#include <sys/socket.h> /* basic socket definitions */
#include <netinet/in.h> /* sockaddr_in{} and other Internet defns */
#include <arpa/inet.h> /* inet(3) functions */
#include <stdlib.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <poll.h> /* poll function */
#include <limits.h>
#define MAXLINE 10240
#ifndef OPEN_MAX
#define OPEN_MAX 40960
#endif
void handle(struct pollfd* clients, int maxClient, int readyClient);
int main(int argc, char **argv)
{
int servPort = 6888;
int listenq = 1024;
int listenfd, connfd;
struct pollfd clients[OPEN_MAX];
int maxi;
socklen_t socklen = sizeof(struct sockaddr_in);
struct sockaddr_in cliaddr, servaddr;
char buf[MAXLINE];
int nready;
bzero(&servaddr, socklen);
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(servPort);
listenfd = socket(AF_INET, SOCK_STREAM, 0);
if (listenfd < 0) {
perror("socket error");
}
int opt = 1;
if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) {
perror("setsockopt error");
}
if(bind(listenfd, (struct sockaddr *) &servaddr, socklen) == -1) {
perror("bind error");
exit(-1);
}
if (listen(listenfd, listenq) < 0) {
perror("listen error");
}
clients[0].fd = listenfd;
clients[0].events = POLLIN;
int i;
for (i = 1; i< OPEN_MAX; i++)
clients[i].fd = -1;
maxi = listenfd + 1;
printf("pollechoserver startup, listen on port:%d\n", servPort);
printf("max connection is %d\n", OPEN_MAX);
for ( ; ; ) {
nready = poll(clients, maxi + 1, -1);
//printf("nready is %d\n", nready);
if (nready == -1) {
perror("poll error");
}
if (clients[0].revents & POLLIN) {
connfd = accept(listenfd, (struct sockaddr *) &cliaddr, &socklen);
sprintf(buf, "accept form %s:%d\n", inet_ntoa(cliaddr.sin_addr), cliaddr.sin_port);
printf(buf, "");
for (i = 0; i < OPEN_MAX; i++) {
if (clients[i].fd == -1) {
clients[i].fd = connfd;
clients[i].events = POLLIN;
break;
}
}
if (i == OPEN_MAX) {
fprintf(stderr, "too many connection, more than %d\n", OPEN_MAX);
close(connfd);
continue;
}
if (i > maxi)
maxi = i;
--nready;
}
handle(clients, maxi, nready);
}
}
void handle(struct pollfd* clients, int maxClient, int nready) {
int connfd;
int i, nread;
char buf[MAXLINE];
if (nready == 0)
return;
for (i = 1; i< maxClient; i++) {
connfd = clients[i].fd;
if (connfd == -1)
continue;
if (clients[i].revents & (POLLIN | POLLERR)) {
nread = read(connfd, buf, MAXLINE);//讀取用戶端socket流
if (nread < 0) {
perror("read error");
close(connfd);
clients[i].fd = -1;
continue;
}
if (nread == 0) {
printf("client close the connection");
close(connfd);
clients[i].fd = -1;
continue;
}
write(connfd, buf, nread);//響應用戶端
if (--nready <= 0)//沒有連接配接需要處理,退出循環
break;
}
}
}
4.總結讨論
1.poll與select,epoll比較