IO多路服用poll

参考:《UNIX 网络编程 · 卷1 : 套接字联网API》

poll

poll 提供了与 select 相似的功能,不过在处理流设备时,能提供额外的信息。

poll相关接口

需要的头文件:

#include <poll.h>

需要的函数:

int poll(struct pollfd *fdarray, unsigned long nfds, int timeout);

fdarray 是指向结构体 struct pollfd 数组第一个元素的指针。每个数组元素都是一个 pollfd 结构,nfds 决定数组中元素的个数,timeout 指出 poll 函数返回前等待的时间(INFTIM:永远等待;0:立即返回不阻塞;>0:等待指定数目的毫秒数)。

其中 struct pollfd 定义如下:

struct pollfd
{
	int fd;			//文件描述符
	short int events;		//关心的事件
	short int revents;		//发生的事件
};

poll 函数返回值:发生错误返回 -1,没有任何事件发生返回 0,否则返回有事件发生的描述符的个数。

如果我们不关心某个描述符,可以将它的 pollfd 结构的 fd 成员设置成一个负值。poll 函数将忽略这样的 pollfd 结构的 event 成员,返回时将它的 reverts 成员的值置为 0。

在 select 中,每个描述符的大小受到了 FD_SETSIZE 限制,但在 poll 中就不会有此问题了,因为分配一个 pollfd 结构的数组并把该数组中元素的数目通知内核成了调用者的责任。内核不再需要知道类似 fd_set 的固定大小的数据类型。

从可移植性考虑,支持 select 的系统比支持 poll 的系统多,另外 posix 还定义了 pselect,能处理信号阻塞并提供了更高时间分辨率的 select 增强版本,但 poll 没有类似的东西。

poll 的事件

struct pollfd 结构体关心的事件由 events 指定,在相应的 revents 成员中返回该描述符的状态。每个描述符结构体都有两个变量,从而避免了使用传入-传出参数(而 select 的设计三个参数都是传入-传出参数)。events 和 revents 的一些常值如下:

常值能否作为events的输入能否作为revents的结果说明
POLLINYESYES普通或优先级带数据可读
POLLRDNORMYESYES普通数据可读
POLLRDBANDYESYES优先级带数据可读
POLLPRIYESYES高优先级数据可读
POLLOUTYESYES普通数据可读
POLLWRNORMYESYES普通数据可写
POLLWRBANDYESYES优先级带数据可写
POLLERRNOYES发生错误
POLLHUBNOYES发生挂起
POLLNVALNOYES描述符不是一个打开的文件

前四个是处理输入的四个常值,中三个是处理输出的常值,后三个是处理错误的三个常值。

poll 识别三类数据:普通(normal)、优先级带(priority)、高级优先(high priority)。

TCP 套接字和 UDP 套接字以下条件会引起 poll 返回特定的 revent:

  • 所有正规 TCP 数据和 UDP 数据都被认为是普通数据。

  • TCP 的带外数据被认为是优先级带数据。

  • TCP 连接的读半部关闭时,也被认为是普通数据,随后的读操作将返回 0。

  • TCP 连接存在错误即可认为是普通数据,也可认为是错误。无论哪种情况,随后的读操作都将返回 -1,并设置 errno 值。

  • 在监听套接字上有新的链接可用即可认为是普通的数据,也可认为是优先级数据。

  • 非阻塞式的 connect 的完成被认为使相应的套接字可写。

poll 实例

#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
#include <sys/wait.h>
#include <poll.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <vector>
#include <iostream>

using namespace std;

#define ERR_EXIT(m)         \
    do                      \
    {                       \
        perror(m);          \
        exit(EXIT_FAILURE); \
    } while (0)

typedef std::vector<struct pollfd> POLLFDLIST;

int main(int argc, char **argv)
{
    signal(SIGPIPE, SIG_IGN);
    signal(SIGCHLD, SIG_IGN);
    int idlefd = open("/dev/null", O_RDONLY | O_CLOEXEC);

    //创建套接字
    int listenfd;
    if ((listenfd = socket(PF_INET, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, IPPROTO_TCP)) < 0)
        ERR_EXIT("socket.");
    //填充服务器地址信息
    struct sockaddr_in servaddr;
    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(8000);
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    //地址&端口复用
    int on = 1;
    if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)
        ERR_EXIT("setsockopt reuseaddr.");
    if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(on)) < 0)
        ERR_EXIT("setsockopt reuseport.");
    //绑定
    if (bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0)
        ERR_EXIT("bind.");
    //监听
    if (listen(listenfd, SOMAXCONN) < 0)
        ERR_EXIT("listen.");

    struct pollfd pfd;
    pfd.fd = listenfd;
    pfd.events = POLLIN;

    POLLFDLIST pollfds;
    pollfds.push_back(pfd);

    int nready;
    struct sockaddr_in peeraddr;
    socklen_t peerlen;
    int connfd;

    while (1)
    {
        nready = poll(&*pollfds.begin(), pollfds.size(), -1);
        if (nready == -1)
        {
            if (errno == -1)
                continue;
            ERR_EXIT("poll.");
        }
        if (nready == 0)
        {
            continue;
        }
        if (pollfds[0].revents & POLLIN) //新的客户端连接
        {
            peerlen = sizeof(peeraddr);
            connfd = accept4(listenfd, (struct sockaddr *)&peeraddr, &peerlen, SOCK_NONBLOCK | SOCK_CLOEXEC);

            if (connfd == -1)
            {
                if (errno == EMFILE) //描述符被使用完,应该关闭与对方的连接
                {
                    close(idlefd);
                    idlefd = accept(listenfd, NULL, NULL);
                    close(idlefd);
                    idlefd = open("dev/null", O_RDONLY | O_CLOEXEC);
                    continue;
                }
                else
                {
                    ERR_EXIT("accept4.");
                }
            }
            //将新连接的套接字描述符以监听事件加入到pollfds中
            pfd.fd = connfd;
            pfd.events = POLLIN;
            pfd.revents = 0;
            pollfds.push_back(pfd);
            --nready;
            //连接成功
            cout << "ip=" << inet_ntoa(peeraddr.sin_addr) << "port=" << ntohs(peeraddr.sin_port) << endl;
            if (nready == 0) //只有一个描述符有事件且该事件为接受新的客户端
                continue;
        }

        for (POLLFDLIST::iterator it = pollfds.begin() + 1; it != pollfds.end() && nready > 0; ++it)
        {
            if (it->revents & POLLIN) //检查可读事件
            {
                --nready;
                connfd = it->fd;
                char buf[1024] = {0};
                int ret = read(connfd, buf, 1024);
                if (ret == -1)
                    ERR_EXIT("read.");
                if (ret == 0) //对方断开连接
                {
                    cout << "client close." << endl;
                    it = pollfds.erase(it);
                    --it;
                    close(connfd);
                    continue;
                }

                cout << buf << endl;
                write(connfd, buf, strlen(buf));
            }
        }
    }

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
水资源是人类社会的宝贵财富,在生活、工农业生产中是不可缺少的。随着世界人口的增长及工农业生产的发展,需水量也在日益增长,水已经变得比以往任何时候都要珍贵。但是,由于人类的生产和生活,导致水体的污染,水质恶化,使有限的水资源更加紧张。长期以来,油类物质(石油类物质和动植物油)一直是水和土壤中的重要污染源。它不仅对人的身体健康带来极大危害,而且使水质恶化,严重破坏水体生态平衡。因此各国都加强了油类物质对水体和土壤的污染的治理。对于水中油含量的检测,我国处于落后阶段,与国际先进水平存在差距,所以难以满足当今技术水平的要求。为了取得具有代表性的正确数据,使分析数据具有与现代测试技术水平相应的准确性和先进性,不断提高分析成果的可比性和应用效果,检测的方法和仪器是非常重要的。只有保证了这两方面才能保证快速和准确地测量出水中油类污染物含量,以达到保护和治理水污染的目的。开展水中油污染检测方法、技术和检测设备的研究,是提高水污染检测的一条重要措施。通过本课题的研究,探索出一套适合我国国情的水质污染现场检测技术和检测设备,具有广泛的应用前景和科学研究价值。 本课题针对我国水体的油污染,探索一套检测油污染的可行方案和方法,利用非分散红外光度法技术,开发研制具有自主知识产权的适合国情的适于野外便携式的测油仪。利用此仪器,可以检测出被测水样中亚甲基、甲基物质和动植物油脂的污染物含量,为我国众多的环境检测站点监测水体的油污染状况提供依据。
### 内容概要 《计算机试卷1》是一份综合性的计算机基础和应用测试卷,涵盖了计算机硬件、软件、操作系统、网络、多媒体技术等多个领域的知识点。试卷包括单选题和操作应用两大类,单选题部分测试学生对计算机基础知识的掌握,操作应用部分则评估学生对计算机应用软件的实际操作能力。 ### 适用人群 本试卷适用于: - 计算机专业或信息技术相关专业的学生,用于课程学习或考试复习。 - 准备计算机等级考试或职业资格认证的人士,作为实战演练材料。 - 对计算机操作有兴趣的自学者,用于提升个人计算机应用技能。 - 计算机基础教育工作者,作为教学资源或出题参考。 ### 使用场景及目标 1. **学习评估**:作为学校或教育机构对学生计算机基础知识和应用技能的评估工具。 2. **自学测试**:供个人自学者检验自己对计算机知识的掌握程度和操作熟练度。 3. **职业发展**:帮助职场人士通过实际操作练习,提升计算机应用能力,增强工作竞争力。 4. **教学资源**:教师可以用于课堂教学,作为教学内容的补充或学生的课后练习。 5. **竞赛准备**:适合准备计算机相关竞赛的学生,作为强化训练和技能检测的材料。 试卷的目标是通过系统性的题目设计,帮助学生全面复习和巩固计算机基础知识,同时通过实际操作题目,提高学生解决实际问题的能力。通过本试卷的学习与练习,学生将能够更加深入地理解计算机的工作原理,掌握常用软件的使用方法,为未来的学术或职业生涯打下坚实的基础。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值