linux poll用法

相对于select来说,poll 也是在指定时间内论询一定数量的文件描述符,来测试其中是否有就绪的,不过,poll 提供了一个易用的方法,来实现 i/o 复用。

声明如下:

       #include <poll.h>

       int poll(struct pollfd *fds, nfds_t nfds, int timeout);

  其中,struct pollfd 定义为:

           struct pollfd {
               int   fd;         /* file descriptor */
               short events;     /* requested events */
               short revents;    /* returned events */
           };

  fd 为文件描述符,events 告诉poll 监听fd 上哪些事件,它是一系列事件按位或。revents 由内核修改,来通知应用程序fd 上实际上发生了哪些事件。

nfds 为监听事件集合fds的大小

timeout 为poll的超时时间,单位毫秒。timeout 为-1时,poll永远阻塞,直到有事件发生。timeout为0时,poll立即返回。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <poll.h>
#include <errno.h>
#include <fcntl.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <unistd.h>

#include <map>
#include <string>

using namespace std;

#define BUFSIZE 10
#define CLIENTSIZE 100


int createSocket()
{
    struct sockaddr_in servaddr;
    int listenfd = -1;

    if (-1 == (listenfd = socket(PF_INET, SOCK_STREAM, 0)))
    {
        fprintf(stderr, "socket: %d, %s\n", errno, strerror(errno));
        exit(1);
    }

    int reuseaddr = 1;
    if (-1 == setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof(reuseaddr)))
    {
        fprintf(stderr, "setsockopt: %d, %s\n", errno, strerror(errno));
        exit(1);
    }

    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family = PF_INET;
    servaddr.sin_port = htons(8008);
    inet_pton(PF_INET, "0.0.0.0", &servaddr.sin_addr);

    if (-1 == bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)))
    {
        fprintf(stderr, "bind: %d, %s\n", errno, strerror(errno));
        exit(1);
    }

    if (-1 == listen(listenfd, 5))
    {
        fprintf(stderr, "listen: %d, %s\n", errno, strerror(errno));
        exit(1);
    }

    return listenfd;
}

int setnoblock(int fd)
{
    int oldopt = fcntl(fd, F_GETFL);
    int newopt = oldopt | O_NONBLOCK;
    fcntl(fd, F_SETFL, newopt);
    return oldopt;
}

int main()
{
    struct pollfd fds[CLIENTSIZE];
    int listenfd = createSocket();

    map<int, string> mapdata;

    fds[0].fd = listenfd;
    fds[0].events = POLLIN | POLLERR;
    fds[0].revents = 0;
    int conncount = 0;

    while (1)
    {
        int ret = poll(fds, conncount + 1, -1);
        if (ret < 0)
        {
            fprintf(stderr, "poll: %d, %s\n", errno, strerror(errno));
            exit(1);
        }
        
        for (int i = 0; i < conncount + 1; i++)
        {
            // 客户端关闭,或者错误。错误的原因是由于客户端关闭导致的,这里一并处理
            if ((fds[i].revents & POLLRDHUP) || (fds[i].revents & POLLERR))
            {
                int fd = fds[i].fd;
                fds[i] = fds[conncount];
                i--;
                conncount--;
                mapdata.erase(fd);
                close(fd);
                printf("delete connection: %d\n", fd);
            }
            // 新的连接
            else if ((fds[i].fd == listenfd) && (fds[i].revents & POLLIN))
            {
                struct sockaddr_in client;
                socklen_t lenaddr = sizeof(client);
                int conn = -1;

                if (-1 == (conn = accept(listenfd, (struct sockaddr*)&client, &lenaddr)))
                {
                    fprintf(stderr, "accept: %d, %s\n", errno, strerror(errno));
                    exit(1);
                }
                printf("get connection %d from %s:%d\n", conn, inet_ntoa(client.sin_addr), client.sin_port);
                conncount++;
                setnoblock(conn);
                fds[conncount].fd = conn;
                fds[conncount].events = POLLIN | POLLRDHUP | POLLERR;
                fds[conncount].revents = 0;
            }
            // 有可读数据
            else if (fds[i].revents & POLLIN)
            {
                char buf[BUFSIZE] = {0};

                int lenrecv = recv(fds[i].fd, buf, BUFSIZE-1, 0);
                if (lenrecv > 0)
                {
                    mapdata[fds[i].fd] = buf;
                    fds[i].events &= (~POLLIN);
                    fds[i].events |= POLLOUT;
                }
                else if (lenrecv == 0)
                {
                    printf("------- client %d exit (not print) --------\n", fds[i].fd);
                }
                else
                {
                    fprintf(stderr, "recv: %d, %s\n", errno, strerror(errno));
                    exit(1);
                }
            }
            // 可写数据
            else if (fds[i].revents & POLLOUT)
            {
                if (send(fds[i].fd, mapdata[fds[i].fd].c_str(), mapdata[fds[i].fd].size(), 0) < 0)
                {
                    if (ECONNRESET == errno)
                    {
                        continue;
                    }
                    fprintf(stderr, "send: %d, %s\n", errno, strerror(errno));
                    exit(1);
                }
                fds[i].events &= (~POLLOUT);
                fds[i].events |= POLLIN;
            }
        }
    }
}

  

posted @ 2018-09-11 23:09  二狗啸地  阅读(11724)  评论(0编辑  收藏  举报