poll的使用和select类似,select使用一组宏来表示处理描述符集合,而poll使用结构体。
在这里插入代码片
#include <poll.h>
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
struct pollfd {
int fd; //文件描述符
short events;//请求的事件
short revents;//返回的事件
}
在events置位:
POLLIN 普通或优先级带数据可读
POLLRDNORM 普通数据可读
POLLRDBAND 优先级带数据可读
POLLPRI 高优先级数据可读
POLLOUT 普通数据可写
POLLWRNORM 普通数据可写
POLLWRBAND 优先级带数据可写
POLLERR 发生错误
POLLHUP 发生挂起
POLLNVAL 描述字不是一个打开的文件
最后一个参数timeout:是一个用毫秒表示的时间,是指定poll在返回前
没有接收事件时应该等待的时间。如果它的值为-1,poll就永远都不会
超时。如果整数值为32个比特,那么最大的超时周期大约是30分钟。
在这里插入代码片
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <poll.h>
#include <errno.h>
#include <ctype.h>
#define MAXLINE 1024
#define SERV_PORT 9000
#define FILE_MAX 1024
int main(int argc, char *argv[])
{
int i, j, maxi, listenfd, connfd, sockfd;
int nready; /*接收poll返回值, 记录满足监听事件的fd个数*/
ssize_t n;
char buf[MAXLINE], str[INET_ADDRSTRLEN];
socklen_t clilen;
struct pollfd client[FILE_MAX];
struct sockaddr_in cliaddr, servaddr;
listenfd = socket(AF_INET, SOCK_STREAM, 0);
int opt = 1;
setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(SERV_PORT);
if (bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) == -1) {
printf("bind error\n");
exit(-1);
}
if (listen(listenfd, 128) == -1) {
printf("listen error\n");
exit(-1);
}
client[0].fd = listenfd; /* 要监听的第一个文件描述符 存入client[0]*/
client[0].events = POLLIN; /* listenfd监听普通读事件 */
for (i = 1; i < FILE_MAX; i++)
client[i].fd = -1; /* 用-1初始化client[]里剩下元素 0也是文件描述符,不能用 */
maxi = 0; /* client[]数组有效元素中最大元素下标 */
for ( ; ; ) {
nready = poll(client, maxi+1, -1); /* 阻塞监听是否有客户端链接请求 */
if (client[0].revents & POLLIN) { /* listenfd有读事件就绪 */
clilen = sizeof(cliaddr);
connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &clilen);/* 接收客户端请求 Accept 不会阻塞 */
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 < FILE_MAX; i++) {
if (client[i].fd < 0) {
client[i].fd = connfd; /* 找到client[]中空闲的位置,存放accept返回的connfd */
break;
}
}
if (i == FILE_MAX) { /* 达到了最大客户端数 */
printf("too many clients");
exit(-1);
}
client[i].events = POLLIN; /* 设置刚刚返回的connfd,监控读事件 */
if (i > maxi)
maxi = i; /* 更新client[]中最大元素下标 */
if (--nready <= 0)
continue; /* 没有更多就绪事件时,继续回到poll阻塞 */
}
for (i = 1; i <= maxi; i++) { /* 前面的if没满足,说明没有listenfd满足. 检测client[] 看是那个connfd就绪 */
if ((sockfd = client[i].fd) < 0)
continue;
if (client[i].revents & POLLIN) {
if ((n = read(sockfd, buf, MAXLINE)) < 0) {
/* connection reset by client */
if (errno == ECONNRESET) { /* 收到RST标志 */
printf("client[%d] aborted connection\n", i);
close(sockfd);
client[i].fd = -1; /* poll中不监控该文件描述符,直接置为-1即可,不用像select中那样移除 */
} else {
printf("read error");
exit(-1);
}
} else if (n == 0) { /* 说明客户端先关闭链接 */
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]);
write(sockfd, buf, n);
}
if (--nready <= 0)
break;
}
}
}
return 0;
}