poll 使用的结构体如下
struct pollfd {
int fd; /* file descriptor */
short events; /* requested events */
short revents; /* returned events */
};
需要使用该结构体数组对文件描述符进行注册,poll 通过该数组返回结果。事件就绪时需要遍历所有注册的文件描述符。
源码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <poll.h>
#include <sys/stropts.h>
#define SERV_PORT 9000
#define LISTENQ 10
#define OPEN_MAX 1024
#define MAXLINE 1024
int main(int argc, char **argv)
{
int listenfd, connfd, sockfd;
int nready, maxi, i, n;
socklen_t clilen;
struct sockaddr_in cliaddr, servaddr;
struct pollfd client[OPEN_MAX];
char buf[MAXLINE];
/* 创建 socket */
if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
perror("socket");
exit(EXIT_FAILURE);
}
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(SERV_PORT);
/* 绑定地址到 socket */
if (bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) {
perror("bind");
exit(EXIT_FAILURE);
}
/* 转为监听 socket */
if (listen(listenfd, LISTENQ)) {
perror("listen");
exit(EXIT_FAILURE);
}
client[0].fd = listenfd;
client[0].events = POLLRDNORM;
for (i = 1; i < OPEN_MAX; ++i) {
client[i].fd = -1;
}
maxi = 0;
for ( ; ; ) {
nready = poll(client, maxi + 1, 1000);
if (nready < 0) {
perror("poll");
exit(EXIT_FAILURE);
} else if (nready == 0) {
fprintf(stderr, "timeout\n");
}
if (client[0].revents & POLLRDNORM) {
/* 监听 socket 上读事件 */
clilen = sizeof(cliaddr);
connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &clilen);
if (connfd == -1) {
perror("accept");
continue;
}
fprintf(stderr, "accept %d\n", connfd);
/* 从client 数组中找到一个空位 */
for (i = 1; i < OPEN_MAX; i++) {
if (client[i].fd < 0) {
client[i].fd = connfd;
break;
}
}
if (i == OPEN_MAX) {
fprintf(stderr, "too many clients");
exit(EXIT_FAILURE);
}
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;
if (client[i].revents & (POLLRDNORM | POLLERR)) {
/* 读事件 */
if ((n = read(sockfd, buf, MAXLINE)) < 0) {
if (errno == ECONNRESET) {
close(sockfd);
client[i].fd = -1;
} else {
fprintf(stderr, "read error");
}
} else if (n == 0) {
/* 客户端关闭连接 */
close(sockfd);
client[i].fd = -1;
} else {
/* 回写数据到客户端 */
write(sockfd, buf, n);
}
if (--nready <= 0) {
break;
}
}
}
}
return 0;
}
参考资料
《UNP》