poll

本文详细介绍了Linux中poll函数的使用,包括函数原型、参数解析,以及如何利用poll实现多路IO转接的代码示例。通过示例代码展示了如何监听多个文件描述符,突破1024限制,并讨论了poll的优点和缺点。同时,还提到了如何调整系统限制以允许更多的文件描述符打开。
摘要由CSDN通过智能技术生成

1 poll 函数原型

#include <poll.h>
int poll(struct pollfd *fds, nfds_t nfds, int timeout);

struct pollfd {
    int fd;             /* 文件描述符 */
    short events;       /* 监控的事件 */
    short revents;      /* 监控事件中满足条件返回的事件 */
};

/*
 *
 * ★ POLLIN          普通或带外优先数据可读,即POLLRDNORM | POLLRDBAND
 *   POLLRDNORM      数据可读
 *   POLLRDBAND      优先级带数据可读
 *   POLLPRI         高优先级可读数据
 * 
 * ★ POLLOUT     	   普通或带外数据可写
 *   POLLWRNORM      数据可写
 *   POLLWRBAND      优先级带数据可写
 * 
 * ★ POLLERR         发生错误
 *   POLLHUP         发生挂起
 *   POLLNVAL        描述字不是一个打开的文件
 * 
 * nfds            监控数组中有多少文件描述符需要被监控
 * 
 * timeout         毫秒级等待
 *      -1: 阻塞等待,#define INFTIM -1               Linux中没有定义此宏
 *      0:  立即返回,不阻塞进程
 *      >0: 等待指定毫秒数,如当前系统时间精度不够毫秒,向上取值
 *
 * return value:
 *  成功: 返回满足对应监听事件的文件描述符总个数
 *  失败: -1,设置errno
 */

如果不再监控某个文件描述符时,可以把pollfd结构体中,fd设置为-1,poll不再监控此pollfd,下次返回时,把revents设置为0。

相较于select而言,poll的优势:

  1. 传入、传出事件分离。无需每次调用时,重新设定监听事件。
  2. 文件描述符上限,可突破1024限制。能监控的最大上限数可使用配置文件调整。


2 poll实现多路IO转接(代码)

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <ctype.h>
#include <poll.h>
#include <errno.h>


int main()
{
    int listenfd, connfd;   // 监听套接字、连接套接字
    int maxi = 0;           // 数组的最大下标
    listenfd = socket(AF_INET, SOCK_STREAM, 0);
    if (-1 == listenfd)
    {
        perror("socket error");
        exit(1);
    }

    // 端口复用
    int opt = 1;
    setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

    struct sockaddr_in saddr, caddr;

    memset(&saddr, 0, sizeof(saddr));
    saddr.sin_family = AF_INET;
    saddr.sin_port   = htons(8000);
    saddr.sin_addr.s_addr = inet_addr("192.168.71.132");
    int ret = bind(listenfd, (struct sockaddr*)&saddr, sizeof(saddr));
    if (-1 == ret)
    {
        perror("bind error");
        close(listenfd);
        exit(1);
    }

    ret = listen(listenfd, 5);
    if (-1 == ret)
    {
        perror("listen error");
        exit(1);
    }

    struct pollfd client[1024];

    for (int ii = 0; ii < 1024; ++ii)
    {
        client[ii].fd = -1;
        client[ii].events = 0;
        client[ii].revents = 0;
    }

    client[0].fd = listenfd;        // 要监听的第一个文件描述符存入client[0]中
    client[0].events = POLLIN;      // listenfd监听普通读事件

    maxi = 0;

    int nReady = 0;
    while (1)
    {
        nReady = poll(client, maxi+1, -1);  // 阻塞监听
        if (-1 == nReady)
        {
            perror("poll error");
            exit(1);
        }
        else if (0 == nReady)   // poll为阻塞,不会有这种情况
        {
        }
        else
        {
            if (client[0].revents & POLLIN) // 客户端连接请求
            {
                client[0].revents = 0;

                socklen_t caddrLen = sizeof(caddr);
                connfd = accept(listenfd, (struct sockaddr*)&caddr, &caddrLen); // 与客户端连接

                printf("建立连接成功:%s:%d\n", inet_ntoa(caddr.sin_addr), ntohs(caddr.sin_port));

                int ii = 0;
                for (ii = 0; ii < 1024; ++ii)
                {
                    if (-1 == client[ii].fd)          // 找到client[]中空闲的位置,存放accept返回的connfd
                    {
                        client[ii].fd = connfd;
                        client[ii].events = POLLIN;

                        if (ii > maxi)      // 更新client[]中最大元素下标
                        {
                            maxi = ii;
                        }

                        break;
                    }
                }

                if (1024 == ii)     // 达到最大客户端数
                {
                    printf("too many clients\n");
                    exit(1);
                }

                if (--nReady <= 0)  // 没有更多就绪事件,继续回到poll阻塞
                    continue;
            }

            for (int ii = 1; ii <= maxi; ++ii)
            {
                int sockfd = 0;
                if ((sockfd = client[ii].fd) < 0)
                    continue;

                if (client[ii].revents & POLLIN)
                {
                    client[ii].revents = 0;

                    char buf[1024] = {0};
                    int n = read(sockfd, buf, sizeof(buf));
                    if (-1 == n)
                    {
                        if (errno == EINTR || errno == EAGAIN)
                        {
                            continue;
                        }
                        else if (errno == ECONNRESET)   // 收到RST标识
                        {
                            close(sockfd);
                            client[ii].fd = -1;
                        }
                        else
                        {
                            exit(1);
                        }
                    }
                    else if (0 == n)
                    {
                        printf("断开连接\n");
                        close(sockfd);
                        client[ii].fd = -1;
                    }
                    else
                    {
                        printf("recv:%s\n", buf);
                    }

                    if (--nReady <= 0)
                        break;
                }
            }
        }
    }
    return 0;
}


3 poll优缺点

  • 优点

    • 自带数据结构。可以将监听事件集合和返回事件集合分离
    • 可以拓展监听上限。超出1024
  • 缺点

    • 不可跨平台。(Linux/类Unix)


4 文件描述符突破1024限制

可以使用cat命令查看当前计算机所能打开的最大文件个数。受硬件影响

cat /proc/sys/fs/file-max

当前用户下的进程,默认打开文件描述符个数。缺省为 1024

ulimit -a

修改

sudo vim /etc/security/limits.conf

在文件尾部写入以下配置,soft软限制,hard硬限制。(中间空格为<tab>)

* 	soft nofile 3000	-----> 设置默认值,可以直接借助命令修改。(ulimit -n 描述符个数) 【注销用户,使其生效】
* 	hard nofile 20000	-----> 命令修改上限

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值