• 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
    ", errno, strerror(errno));
            exit(1);
        }
    
        int reuseaddr = 1;
        if (-1 == setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof(reuseaddr)))
        {
            fprintf(stderr, "setsockopt: %d, %s
    ", 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
    ", errno, strerror(errno));
            exit(1);
        }
    
        if (-1 == listen(listenfd, 5))
        {
            fprintf(stderr, "listen: %d, %s
    ", 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
    ", 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
    ", 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
    ", errno, strerror(errno));
                        exit(1);
                    }
                    printf("get connection %d from %s:%d
    ", 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) --------
    ", fds[i].fd);
                    }
                    else
                    {
                        fprintf(stderr, "recv: %d, %s
    ", 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
    ", errno, strerror(errno));
                        exit(1);
                    }
                    fds[i].events &= (~POLLOUT);
                    fds[i].events |= POLLIN;
                }
            }
        }
    }
    

      

  • 相关阅读:
    Django笔记:上下文处理器和中间件
    Django笔记:Cookie和Session
    redhat 7.4从openssh7.6离线升级openssh8.4p1解决方法
    “应用程序无法正常启动(0xc000007)”处理办法
    "安装VMware Tools"显示灰色的解决办法
    redis 根据模板批量生成使用不同端口的配置文件并运行运行服务
    扩展 docker 管理命令
    shell getopt 讲解
    编写 Redis 测试 shell 脚本
    自定义 shell 软件安装脚本
  • 原文地址:https://www.cnblogs.com/zuofaqi/p/9631601.html
Copyright © 2020-2023  润新知