IO多路复用之poll篇
前言
前一篇介绍了IO多路复用中的select()
,但是select()
的缺点很明显,监控描述符的个数不能超过1024个,所以后来为了解决这个问题,引入了poll()
,理论上poll()
监听的描述符个数不受限制,但是实际能监控的描述符个数和机器自身的硬件配置有关系。
系统调用
#include <poll.h>
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
poll()
函数的作用和select()
函数的作用相似,都是监控大量的文件描述符,等待其中的一个或多个就绪。只是参数有一些不同。
参数
fds
该参数是指向
pollfd
类型数组的指针,指向了待监控的文件描述符的列表。pollfd
类型的定义如下所述:struct pollfd { int fd; /* file descriptor */ short events; /* requested events */ short revents; /* returned events */ };
其中,各个字段的含义如下:
fd:
代表需要系统监控的文件描述符,需要用户指定该参数。如果该字段为负值,则系统忽略对应的
events
字段的值,并且revents
值被系统赋值为0。events:
代表用户对fd感兴趣的事件集合(位掩码),该字段为输入参数,需要用户指定该参数。
该字段可以取值如下所述:
POLLIN
有数据可读。
POLLPRI
文件描述符上有异常情况。
POLLOUT
文件描述符可写。
- POLLRDHUP
- POLLERR
- POLLHUP
- POLLNVAL
POLLRDNORM
和POLLIN一样。
POLLRDBAND
一般在Linux不用此值。
POLLWRNORM
和POLLOUT相同。
POLLWRBAND
带优先级数据。
revents:
代表在fd上,发生的事件。该字段为内核传出的值。
nfds
用此值来指定数组中元素的个数。
timeout
该参数指定
poll()
阻塞等待的毫秒数。该参数可以取值:
- 大于0:指定阻塞等待的毫秒数;
- 0:不阻塞,立即返回。
返回值
返回值可有如下3种情况:
- 大于0:代表数组中发生事件的元素的个数,即
revents
字段不为0的元素的个数。如果为0的话,代表无调用者感兴趣的事件发生。 - 返回0:代表该调用超时返回。
- 返回-1:代表发生了错误,具体错误可以打印
errno
的值查看。
代码示例
用poll()
写一个回射服务器端,代码示例如下:
#include <arpa/inet.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <poll.h>
#include <limits.h>
#define MAXLINE 4096
#define MAX_OPEN 4094
int main(int argc, char *argv[])
{
int listenfd, maxfd, nready, clientlen;
struct sockaddr_in servaddr, clientaddr;
fd_set rset, allset;
struct pollfd client[MAX_OPEN];
char buf[MAXLINE];
int maxindex;
int i, sockfd, n;
int listenport = 6666;
if (argc == 2)
{
listenport = atoi(argv[1]);
}
listenfd = socket(AF_INET, SOCK_STREAM, 0);
if (listenfd < 0)
{
perror("socket error");
return 0;
}
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(listenport);
if (bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0)
{
perror("bind error.");
return 0;
}
listen(listenfd, SOMAXCONN);
client[0].fd = listenfd;
client[0].events = POLLRDNORM;
for (int i = 1; i < MAX_OPEN; i++)
{
client[i].fd = -1;
}
maxindex = 0;
for (;;)
{
nready = poll(client, maxindex + 1, -1);
if(client[0].revents & POLLRDNORM)
{
clientlen = sizeof(clientaddr);
int connfd = accept(listenfd, (struct sockaddr*)&clientaddr, &clientlen);
for(i = 1;i < MAX_OPEN; i++)
{
if(client[i].fd < 0)
{
client[i].fd = connfd;
client[i].events = POLLRDNORM;
break;
}
}
if(i == MAX_OPEN)
{
printf("too many connections.\n");
close(connfd);
continue;
}
printf("new connection from %s:%d\n",
inet_ntop(AF_INET, &clientaddr.sin_addr, buf, sizeof(buf)),
ntohs(clientaddr.sin_port));
if(i > maxindex)
{
maxindex = i;
}
if(--nready <= 0)
{
continue;
}
}
for (i = 1; i < MAX_OPEN; i++)
{
if( (sockfd = client[i].fd) < 0)
{
continue;
}
if(client[i].revents & (POLLRDNORM | POLLERR))
{
if((n = read(sockfd, buf, sizeof(buf))) < 0)
{
if(getpeername(sockfd, (struct sockaddr*)&clientaddr, &clientlen) == 0)
{
printf("client(%s:%d) read error.",
inet_ntop(AF_INET, &clientaddr.sin_addr, buf, sizeof(buf)),
ntohs(clientaddr.sin_port));
}
if(errno == ECONNRESET) // connection reset by client
{
close(sockfd);
client[i].fd = -1;
perror(NULL);
}
else
{
perror("read error.");
}
}
else if(n == 0)
{
if(getpeername(sockfd, (struct sockaddr*)&clientaddr, &clientlen) == 0)
{
printf("client(%s:%d) close the connection.\n",
inet_ntop(AF_INET, &clientaddr.sin_addr, buf, sizeof(buf)),
ntohs(clientaddr.sin_port));
}
close(sockfd);
client[i].fd = -1;
}
else
{
write(sockfd, buf, n);
}
if(--nready <= 0)
{
break;
}
}
}
}
}
怎么收藏这篇文章?
博主真是太厉害了!!!