【计算机网络】IO多路转接之poll

文章目录

  • 一、poll函数接口
  • 二、socket就绪条件
  • 三、poll的优点
  • 四、poll的缺点
  • 五、poll使用案例--只读取数据的server服务器
    • 1.err.hpp
    • 2.log.hpp
    • 3.sock.hpp
    • 4.pollServer.hpp
    • 5.main.cc

一、poll函数接口

#include <poll.h>
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
// pollfd结构
struct pollfd {
    int fd; /* file descriptor */
    short events; /* requested events */
    short revents; /* returned events */
};

参数说明

fds是一个poll函数监听的结构列表. 每一个元素中, 包含了三部分内容: 文件描述符, 监听的事件集合, 返回的事件集合.

nfds表示fds数组的长度.

timeout表示poll函数的超时时间, 单位是毫秒(ms)

时间单位:ms

1.>0:在timeout以内,阻塞,否则非阻塞返回一次

2.=0:非阻塞等待

3.<0:阻塞等待

events和revents的取值:

在这里插入图片描述

用户 -> 内核 :要帮我关心一下fd。输入看 :fd + events

内核告诉用户:你要关心fd上面的events中有哪些事件已经就绪了。输出时看:fd + revents

这样达到了输入和输出分离,poll不需要对参数进行重新设定,以及解决了select等待fd有上限的问题

返回结果

返回值小于0, 表示出错;

返回值等于0, 表示poll函数等待超时;

返回值大于0, 表示poll由于监听的文件描述符就绪而返回.

二、socket就绪条件

读就绪

socket内核中, 接收缓冲区中的字节数, 大于等于低水位标记SO_RCVLOWAT. 此时可以无阻塞的读该文件描述符, 并且返回值大于0;

socket TCP通信中, 对端关闭连接, 此时对该socket读, 则返回0;

监听的socket上有新的连接请求;

socket上有未处理的错误;

写就绪

socket内核中, 发送缓冲区中的可用字节数(发送缓冲区的空闲位置大小), 大于等于低水位标记

SO_SNDLOWAT, 此时可以无阻塞的写, 并且返回值大于0;

socket的写操作被关闭(close或者shutdown). 对一个写操作被关闭的socket进行写操作, 会触发SIGPIPE信号;

socket使用非阻塞connect连接成功或失败之后;

socket上有未读取的错误;

异常就绪

socket上收到带外数据. 关于带外数据, 和TCP紧急模式相关(TCP协议头中, 有一个紧急指针的字段),

三、poll的优点

不同与select使用三个位图来表示三个fdset的方式,poll使用一个pollfd的指针实现.

pollfd结构包含了要监视的event和发生的event,不再使用select“参数-值”传递的方式. 接口使用比select更方便.

poll并没有最大数量限制 (但是数量过大后性能也是会下降)

四、poll的缺点

poll中监听的文件描述符数目增多时,和select函数一样,poll返回后,需要轮询pollfd来获取就绪的描述符.

每次调用poll都需要把大量的pollfd结构从用户态拷贝到内核中.

同时连接的大量客户端在一时刻可能只有很少的处于就绪状态, 因此随着监视的描述符数量的增长, 其效率也会线性下降.

五、poll使用案例–只读取数据的server服务器

1.err.hpp

#pragma once

enum
{
    USAGE_ERR = 1,
    SOCKET_ERR,
    BIND_ERR,
    LISTEN_ERR
};

2.log.hpp

#pragma once

#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <stdarg.h>

#define NORMAL 0
#define DEBUG 1
#define WARNING 2
#define ERROR 3
#define FATAL 4

#define LOG_NORMAL "./log.txt"
#define LOG_ERR "./err.txt"

#define NUM 1024

const char *to_levelstr(int level)
{
    switch (level)
    {
    case DEBUG:
        return "DEBUG";
    case NORMAL:
        return "NORMAL";
    case WARNING:
        return "WARNING";
    case ERROR:
        return "ERROR";
    case FATAL:
        return "FATAL";
    default:
        return nullptr;
    }
}

void LogMessage(int level, const char *format, ...)
{
    // [日志等级] [时间戳/时间] [pid] [messge]
    char logprofix[NUM];
    snprintf(logprofix, sizeof logprofix, "[%s][%ld][pid:%d]", to_levelstr(level), (long int)time(nullptr), getpid());

    char logcontent[NUM];
    va_list arg;
    va_start(arg, format);

    vsnprintf(logcontent, sizeof logcontent, format, arg);

    std::cout << logprofix << logcontent << std::endl;

    FILE *log = fopen(LOG_NORMAL, "a");
    FILE *error = fopen(LOG_ERR, "a");

    if (log && error)
    {
        FILE *cur = nullptr;
        if (level == DEBUG || level == NORMAL || level == WARNING)
            cur = log;
        if (level == ERROR || level == FATAL)
            cur = error;
        if (cur)
            fprintf(cur, "%s%s\n", logprofix, logcontent);

        fclose(log);
        fclose(error);
    }
}

3.sock.hpp

#pragma once

#include <iostream>
#include <cstring>
#include <string>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include "log.hpp"
#include "err.hpp"

class Sock
{
    static const int backlog = 32;

public:
    // 1. 创建socket文件套接字对象
    static int Socket()
    {
        int sock = socket(AF_INET, SOCK_STREAM, 0);
        if (sock < 0)
        {
            LogMessage(FATAL, "create socket error");
            exit(SOCKET_ERR);
        }
        LogMessage(NORMAL, "create socket success:%d", sock);

        int opt = 1;
        setsockopt(sock, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof opt);

        return sock;
    }

    // 2.bind自己的网络信息
    static void Bind(int sock, const uint16_t &port)
    {
        struct sockaddr_in local;
        memset(&local, 0, sizeof local);

        local.sin_family = AF_INET;
        local.sin_port = htons(port);
        local.sin_addr.s_addr = INADDR_ANY;

        int n = bind(sock, (struct sockaddr *)&local, sizeof local);
        if (n < 0)
        {
            LogMessage(FATAL, "socket bind error");
            exit(BIND_ERR);
        }
        LogMessage(NORMAL, "socket bind success");
    }

    // 3. 设置socket 为监听状态
    static void Listen(int sock)
    {
        int n = listen(sock, backlog);
        if (n < 0)
        {
            LogMessage(FATAL, "socket listen error");
            exit(LISTEN_ERR);
        }
        LogMessage(NORMAL, "socket listen success");
    }

    static int Accept(int listensock, std::string *clientip, uint16_t *clientport)
    {
        struct sockaddr_in peer;
        memset(&peer, 0, sizeof peer);
        socklen_t len = sizeof(peer);

        int sock = accept(listensock, (struct sockaddr *)&peer, &len);
        if (sock < 0)
        {
            LogMessage(ERROR, "socket accept error,next");
        }
        else
        {
            LogMessage(NORMAL, "accept a new link success, get new sock: %d", sock);
            *clientip = inet_ntoa(peer.sin_addr);
            *clientport = ntohs(peer.sin_port);
        }

        return sock;
    }
};

4.pollServer.hpp

#pragma once

#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cerrno>
#include <string>
#include <functional>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <poll.h>

#include "sock.hpp"
#include "log.hpp"
#include "err.h"

namespace poll_ns
{
    const static int defaultport = 8080;
    const static int num = 2048;
    const static int defaultfd = -1;

    typedef std::function<std::string(const std::string &)> func_t;

    class pollServer
    {
    public:
        pollServer(const func_t &func, const uint16_t &port = defaultport)
            : _port(port), _func(func), _listensock(-1), _rfds(nullptr)
        {
        }

        void initServer()
        {
            _listensock = Sock::Socket();
            Sock::Bind(_listensock, _port);
            Sock::Listen(_listensock);

            _rfds = new struct pollfd[num];

            for (int i = 0; i < num; i++)
                ResetItem(i);
            _rfds[0].fd = _listensock;
            _rfds[0].events = POLLIN;
            _rfds[0].revents = 0;
        }

        void ResetItem(int pos)
        {
            _rfds[pos].fd = defaultfd;
            _rfds[pos].events = 0;
            _rfds[pos].revents = 0;
        }
        void Print()
        {
            std::cout << "fd list: ";
            for (int i = 0; i < num; i++)
            {
                if (_rfds[i].fd != defaultfd)
                {
                    std::cout << _rfds[i].fd << " ";
                }
            }
        }
        void Accepter(int listensock)
        {
            std::string clientip;
            uint16_t clientport;
            int sock = Sock::Accept(listensock, &clientip, &clientport);

            if (sock < 0)
                return;
            LogMessage(NORMAL, "accept success[%s:%d]", clientip.c_str(), clientport);

            int i = 0;
            for (; i < num; i++)
            {
                if (_rfds[i].fd == defaultfd)
                    continue;
                else
                    break;
            }
            if (i == num)
            {
                LogMessage(WARNING, "server is full,please wait");
                close(sock);
            }
            else
            {
                _rfds[i].fd = sock;
                _rfds[i].events = POLLIN;
                _rfds[i].revents = 0;
            }
            Print();
        }
        void Recver(int pos)
        {
            // 1. 读取request
            char buffer[1024];
            ssize_t s = recv(_rfds[pos].fd, buffer, sizeof(buffer) - 1, 0); // 这里在进行读取的时候,会不会被阻塞?1, 0
            if (s > 0)
            {
                buffer[s] = 0;
                LogMessage(NORMAL, "client# %s", buffer);
            }
            else if (s == 0)
            {
                close(_rfds[pos].fd);
                ResetItem(pos);
                LogMessage(NORMAL, "client quit");
                return;
            }
            else
            {
                close(_rfds[pos].fd);
                ResetItem(pos);
                LogMessage(ERROR, "client error:%s", strerror(errno));
                return;
            }

            // 2. 处理request
            std::string response = _func(buffer);

            // 3. 返回response
            write(_rfds[pos].fd, response.c_str(), response.size());
        }

        void HandlerEvent()
        {
            for (int i = 0; i < num; i++)
            {
                // 过滤掉非法的fd
                if (_rfds[i].fd == defaultfd)
                    continue;
                if (!(_rfds[i].events & POLLIN))
                    continue;
                if (_rfds[i].fd == _listensock && (_rfds[i].events & POLLIN))
                    Accepter(_listensock);
                else if (_rfds[i].events & POLLIN)
                    Recver(i);
                else
                {
                }
            }
        }

        void start()
        {
            for (;;)
            {
                int timeout = -1;
                int n = poll(_rfds, num, timeout);
                switch (n)
                {
                case 0:
                    LogMessage(NORMAL, "timeout...");
                    break;
                case -1:
                    LogMessage(WARNING, "select error,code: %d, err string: %s", errno, strerror(errno));
                    break;
                default:
                    // 说明有事件就绪了,目前只有一个监听事件就绪了
                    LogMessage(NORMAL, "have event ready!");
                    HandlerEvent();
                    break;
                }
            }
        }
        ~pollServer()
        {
            if (_listensock > 0)
                close(_listensock);
            if (_rfds)
                delete[] _rfds;
        }

    private:
        int _port;
        int _listensock;
        struct pollfd *_rfds;
        func_t _func;
    };
}

5.main.cc

#include "pollServer.hpp"
#include "err.hpp"
#include <memory>

using namespace std;
using namespace poll_ns;

static void Usage(const string proc)
{
    std::cerr << "Usage:\n\t" << proc << " port\n\n";
}

string transaction(const string &request)
{
    return "pollServer# " + request;
}

int main(int argc, char *argv[])
{
    // if (argc != 2)
    // {
    //     Usage(argv[0]);
    //     exit(USAGE_ERR);
    // }

    std::unique_ptr<pollServer> svr(new pollServer(transaction));
    svr->initServer();
    svr->start();

    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/428937.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

EmoLLM(心理健康大模型)——探索心灵的深海,用智能的语言照亮情感的迷雾。

文章目录 介绍&#xff1a;应用地址&#xff1a;模型地址&#xff1a;Github地址&#xff1a;视频介绍&#xff1a;效果图&#xff1a; 介绍&#xff1a; EmoLLM是一个基于 InternLM 等模型微调的心理健康大模型&#xff0c;它涵盖了认知、情感、行为、社会环境、生理健康、心…

Python绘制不同形状词云图

目录 1.基本词云图1.1 导入所需库1.2 准备词汇1.3 配置参数并生成词云图1.4 在Python窗口中显示图片1.5 效果展示1.6 完整代码 2. 不同形状词云图2.1 找到自己所需形状图片2.2 利用PS将图片设置为黑白色2.3 在代码中设置背景2.4 效果展示 1.基本词云图 1.1 导入所需库 import…

2023全球软件开发大会-上海站:探索技术前沿,共筑未来软件生态(附大会核心PPT下载)

随着信息技术的迅猛发展&#xff0c;全球软件开发大会&#xff08;QCon&#xff09;已成为软件行业最具影响力的年度盛会之一。2023年&#xff0c;QCon再次来到上海&#xff0c;汇聚了众多业界精英、技术领袖和开发者&#xff0c;共同探讨软件开发的最新趋势和实践。 一、大会…

IO接口 2月5日学习笔记

1.fgetc 用于从文件中读取一个字符&#xff0c;fgetc 函数每次调用将会返回当前文件指针所指向的字符&#xff0c;并将文件指针指向下一个字符。 int fgetc(FILE *stream); 功能: 从流中读取下一个字符 参数: stream:文件流指针 返回值: …

嵌入式驱动学习第二周——断言机制

前言 这篇博客来聊一聊C/C的断言机制。 嵌入式驱动学习专栏将详细记录博主学习驱动的详细过程&#xff0c;未来预计四个月将高强度更新本专栏&#xff0c;喜欢的可以关注本博主并订阅本专栏&#xff0c;一起讨论一起学习。现在关注就是老粉啦&#xff01; 目录 前言1. 断言介绍…

鸿蒙实战应用开发:【拨打电话】功能

概述 本示例通过输入电话&#xff0c;进行电话拨打&#xff0c;及电话相关信息的显示。 样例展示 涉及OpenHarmony技术特性 网络通信 基础信息 拨打电话 介绍 本示例使用call相关接口实现了拨打电话并显示电话相关信息的功能 效果预览 使用说明 1.输入电话号码后&#…

枚举与尺取法(蓝桥杯 c++ 模板 题目 代码 注解)

目录 组合型枚举&#xff08;排列组合模板&#xff08;&#xff09;&#xff09;: 排列型枚举&#xff08;全排列&#xff09;模板&#xff1a; 题目一&#xff08;公平抽签 排列组合&#xff09;&#xff1a; ​编辑 代码&#xff1a; 题目二&#xff08;座次问题 全排…

寒假作业Day 06

寒假作业Day 06 一、选择题 1、关于内存管理&#xff0c;以下有误的是&#xff08; &#xff09; A: malloc在分配内存空间大小的时候是以字节为单位 B: 如果原有空间地址后面还有足够的空闲空间用来分配&#xff0c;则在原有空间后直接增加新的空间&#xff0c;使得增加新空…

No matching version found for @babel/traverse@^7.24.0.

问题&#xff1a; npm安装 依赖失败&#xff0c;找不到所需依赖。 原因&#xff1a; npm镜像源中没有该依赖。&#xff08;大概率是因为依赖最近刚更新&#xff0c;当前镜像源没有同步&#xff09; 解决&#xff1a; 查看自己的npm镜像&#xff1a;npm config get registry…

【刷题记录】详谈设计循环队列

下题目为个人的刷题记录&#xff0c;在本节博客中我将详细谈论设计循环队列的思路&#xff0c;并给出代码&#xff0c;有需要借鉴即可。 题目&#xff1a;LINK 下面是思路分析: 首先一开始收到实现普通队列的思路影响上题目中循环这俩字&#xff0c;那自然想到的是用链表来设计…

IDEA自动导入provided的依赖

最近在学习flink 流程序&#xff0c;在写demo程序的时候依赖flink依赖&#xff0c;依赖的包在flink集群里面是自己已经提供了的&#xff0c;在导入的时候配置为provided&#xff0c;像下面这样&#xff0c;以使打包的时候不用打到最终的程序包里面。 <dependency><gro…

带你从Spark官网啃透Spark Structured Streaming

By 远方时光原创&#xff0c;可转载&#xff0c;open 合作微信公众号&#xff1a;大数据左右手 本文是基于spark官网结构化流解读 Structured Streaming Programming Guide - Spark 3.5.1 Documentation (apache.org) spark官网对结构化流解释 我浓缩了一些关键信息&#xff…

laravel8配合jwt

composer 安装包 composer require tymon/jwt-authconfig/app.php 注册服务提供者 providers > [Tymon\JWTAuth\Providers\LaravelServiceProvider::class, ]aliases > [JWTAuth > Tymon\JWTAuth\Facades\JWTAuth::class,JWTFactory > Tymon\JWTAuth\Facades\JWT…

如何把已安装的nodejs高版本降级为低版本

第一步.先清空本地安装的node.js版本 按健winR弹出窗口&#xff0c;键盘输入cmd,然后敲回车&#xff08;或者鼠标直接点击电脑桌面最左下角的win窗口图标弹出&#xff0c;输入cmd再点击回车键&#xff09; 然后进入命令控制行窗口&#xff0c;并输入where node查看之前本地安装…

[java] 23种设计模式之桥接模式

一、什么是桥接模式 桥接(Bridge)模式属于结构型设计模式。通过提供抽象化和实现化之间的桥接结构&#xff0c;来实现二者的解耦。把抽象(abstraction)与行为实现(implementation)分离开来&#xff0c;从而可以保持各部分的独立性以及应对它们的功能扩展。 二、适用场景 当一…

《互联网的世界》第四讲-拥塞控制与编码

需要澄清的一个误区是&#xff0c;拥塞绝不是发送的数据量太大导致&#xff0c;而是数据在极短的时间段内到达了同一个地方以至于超过了网络处理容量导致&#xff0c;拥塞的成因一定要考虑时间因素。换句话说&#xff0c;拥塞由大突发导致。 只要 pacing&#xff0c;再多的数据…

软考59-上午题-【数据库】-小结+杂题

一、杂题 真题1&#xff1a; 真题2&#xff1a; 真题3&#xff1a; 真题4&#xff1a; 真题5&#xff1a; 真题6&#xff1a; 真题7&#xff1a; 真题8&#xff1a; 二、数据库总结 考试题型&#xff1a; 1、选择题&#xff08;6题&#xff0c;6分&#xff09; 2、综合分析题…

python实现手机号归属地查询

手机上突然收到了某银行的短信提示&#xff0c;看了一下手机的位数&#xff0c;正好是11位。我一想&#xff0c;这不就是标准的手机号码吗&#xff1f;于是一个想法涌上心头——用python的库实现查询手机号码归属地查询自由。 那实现的效果如下&#xff1a; 注&#xff1a;电…

五、软考-系统架构设计师笔记-信息安全技术基础知识

信息安全技术基础知识 1、信息安全基础知识概述 信息安全的概念 信息安全包括 5 个基本要素&#xff1a; 机密性:确保信息不暴露给未授权的实体或进程。完整性:只有得到允许的人才能修改数据&#xff0c;并且能够判别出数据是否已被篡改。可用性:得到授权的实体在需要时可以…

“祖传代码“的是是非非

程序员眼中的“祖传代码”&#xff0c;就像一本古老而神秘的魔法书&#xff0c;藏着无穷的智慧和技巧&#xff0c;有些代码像家传宝贝&#xff0c;有些像祖传秘方。快来分享一下你遇到的“祖传代码”吧~ 祖传代码的历史与文化价值 祖传代码通常指的是经过长时间使用和传承的代…