多路复用之POLL
poll机制的引入:
主要解决select中的文件描述符集上限FD_SETSIZE(1024);原理跟select类似,也是采用轮询形式。
API:
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
\
参数1 struct pollfd {
int fd; /* 文件描述符*/
short events; /* 监控的事件*/
short revents; /* 监控事件中满足条件返回的事件*/
};
参数2 监控文件描述符的数量
参数3 0为非阻塞,-1为阻塞,>0为阻塞时间(毫秒级等待)
/
该API用法相对比select要简单,
- 每次select调用要对描述符集进行备份,已恢复描述符集的状态,而poll的events为要监控的对象,revents为保存事件发生后的文件描述符集。
- 参数1为结构体数组,每个文件描述符对应一个。监控时数组时判断fd的值域,一般用-1表示空,>=0则为有对象。
- 事件的状态(带外优先一般表示socket的读写)
POLLIN普通或带外优先数据可读,即POLLRDNORM | POLLRDBAND
POLLRDNORM-数据可读
POLLRDBAND-优先级带数据可读
POLLPRI 高优先级可读数据
POLLOUT普通或带外数据可写
POLLWRNORM-数据可写
POLLWRBAND-优先级带数据可写
POLLERR 发生错误
POLLHUP 发生挂起
POLLNVAL 描述字不是一个打开的文件
扩展:ppoll GNU定义了ppoll(非POSIX标准),可以支持设置信号屏蔽字
附上代码:
#include <stdio.h>
#include <stdlib.h>
#include <poll.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "wrap.h"
#include <errno.h>
#include <string.h>
#define SERV_PORT 8000
#define MAX_LINE 128
#define OPEN_MAX 1024
int main(int argc, char *argv[])
{
int serv_fd, connfd, i, j, maxi, sockfd, nready;
ssize_t n;
char buf[MAX_LINE], str[INET_ADDRSTRLEN];
struct sockaddr_in servaddr,clientaddr;
socklen_t clilen;
struct pollfd client[OPEN_MAX];
serv_fd = Socket(AF_INET, SOCK_STREAM, 0);
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(SERV_PORT);
Bind(serv_fd, (struct sockaddr *)&servaddr, sizeof(servaddr));
Listen(serv_fd, 20);
client[0].fd = serv_fd;
client[0].events = POLLRDNORM;
for(i = 1; i < OPEN_MAX; i++)
client[i].fd = -1;
maxi = 0;
for( ; ; ){
nready = poll(client, maxi + 1, -1);
if(client[0].revents & POLLRDNORM){
clilen = sizeof(clientaddr);
connfd = Accept(serv_fd, (struct sockaddr *)&clientaddr, &clilen );
for(i = 1; i < OPEN_MAX; i++)
if(client[i].fd < 0){
client[i].fd = connfd;
break;
}
if(i == OPEN_MAX)
perr_exit("too many client");
client[i].events = POLLRDNORM;
if(i > maxi) maxi = i;
if(--nready <= 0) continue;
}
for(i = 1; i <= maxi; i++){
if((sockfd = client[i].fd) < 0)
continue;
else if(client[i].revents & (POLLRDNORM | POLLERR))
{
if((n = Read(sockfd, buf, MAX_LINE)) < 0)
{
if(errno == ECONNRESET){
printf("cli[%d] abort\n",i);
Close(sockfd);
client[i].fd = -1;
}else
perr_exit("read error");
}
else if(n == 0){
printf("cli[%d] closed\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;
}
}
}
return 0;
}