poll

poll相关的api:

// 把所有的文件描述符放到pollfd数组里面,每调用一次poll,需要对所有的pollfd数组做一次遍历。
// "fds"参数是struct pollfd数组
extern int poll (struct pollfd *__fds, nfds_t __nfds, int __timeout);

/* pollfd封装了感兴趣的事件和实际发生的事件 */
/* Data structure describing a polling request.  */
struct pollfd
{
    int fd;            /* File descriptor to poll.  */
    short int events;        /* Types of events poller cares about.  */
    short int revents;        /* Types of events that actually occurred.  */
};

/* Type used for the number of file descriptors.  */
typedef unsigned long int nfds_t;

网上搜到的代码示例:

#include <stdio.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <poll.h>
#include <errno.h>

#define MAXLINE 512
#define OPEN_MAX 1024
#define SA struct sockaddr

int main() {
    int listenfd, connfd, sockfd, i, maxi;
    int nready;
    socklen_t clilen;
    ssize_t n;
    char buf[MAXLINE];
    //我们关心的文件描述符
    struct pollfd client[OPEN_MAX];
    struct sockaddr_in servaddr, cliaddr;
    //创建监听套接字
    if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        printf("socket() error!");
        exit(0);
    }
    //对协议地址进行清零
    bzero(&servaddr, sizeof(servaddr));
    //设置为 IPv4 or IPv6
    servaddr.sin_family = AF_INET;
    //绑定本地端口号
    servaddr.sin_port = htons(9999);
    //任何一个 IP 地址,让内核自行选择
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    //绑定套接口到本地协议地址
    if (bind(listenfd, (SA *) &servaddr, sizeof(servaddr)) < 0) {
        printf("bind() error!");
        exit(0);
    }
    //服务器开始监听
    if (listen(listenfd, 5) < 0) {
        printf("listen() error!");
        exit(0);
    }
    //监听套接字
    client[0].fd = listenfd;
    client[0].events = POLLRDNORM; //读事件
    for (i = 1; i < OPEN_MAX; ++i) {
        client[i].fd = -1;
    }
    maxi = 0;

    for (;;) {
        //poll函数的返回值:发生了事件的文件描述符的个数, 它并没有指出哪几个描述符发生了事件.
        //timeout参数为-1,poll阻塞直到描述符有事件发生
        nready = poll(client, maxi + 1, -1);
        //首先检查监听套接字,查看是否有新连接进来
        if (client[0].revents & POLLRDNORM) {
            clilen = sizeof(cliaddr);
            connfd = accept(listenfd, (SA *) &cliaddr, &clilen);
            if (connfd < 0) {
                continue;
            }
            for (i = 1; i < OPEN_MAX; ++i) {
                if (client[i].fd < 0) {
                    client[i].fd = connfd;
                    //printf does not work
                    fprintf(stdout, "accept a new client
");
                    break;
                }
            }
            if (i == OPEN_MAX) {
                fprintf(stdout, "too many clients");
                exit(0);
            }
            client[i].events = POLLRDNORM;
            if (i > maxi) {
                maxi = i;
            }
            if (--nready <= 0)    continue;
        }
        //然后检查连接套接字
        for (i = 1; i < OPEN_MAX; ++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);
                        printf("1.close fd %d!
", client[i].fd);
                        client[i].fd = -1;
                    } else {
                        printf("read error!
");
                    }
                } else if (n == 0) {
                    close(sockfd);
                    printf("2.close fd %d!
", client[i].fd);
                    client[i].fd = -1;
                } else {
                    write(sockfd, buf, n);
                }
                if (--nready <= 0) break;
            }
        }
    }
}

使用nc命令作为客户端进行交互:nc localhost 9999

从这个例子中,能看出poll的缺点。

poll函数返回发生了事件的描述符个数,我们需要遍历所有的描述符,才能知道具体描述符哪些真实发生了事件。

原文地址:https://www.cnblogs.com/allenwas3/p/7904305.html