Linux学习记录——사십이 高级IO(3)--- Poll型服务器

文章目录

  • 1、认识poll接口
  • 2、实现
  • 3、特点


1、认识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 */
};

poll返回值和select一样,都是int,表示就绪的fd数量。timeout是一个输入型参数,单位是毫秒ms,为0表示非阻塞,小于0表示阻塞,大于0poll在这段时间内阻塞等待,如果一直没有事件就绪,那么超过时间就返回0。fds相当于一个数组,events是用户关心的fd上的事件,revents是内核告诉用户,关心的fd中哪些已经有事件就绪。

poll分离了输入参数和输出参数,这样就不需要在调用poll时进行重新设置了。

在这里插入图片描述

上图的事件其实都是宏,POLLIN表示读事件就绪,POLLOUT表示写事件就绪。

2、实现

基于上一篇select的代码来实现,代码只用到能够等待多个fd那里。基本代码

#pragma once

#include <iostream>
#include <string>
#include <cstring>
#include <sys/poll.h>
#include "Sock.hpp"
#include "log.hpp"
#include "err.hpp"

const static int gport = 8888;

typedef int type_t;

class PollServer
{
    static const int N = (sizeof(fd_set) * 8);

public:
    PollServer(uint16_t port = gport) : port_(port) 
    {
    }
    void InitServer()
    {
        listensock_.Socket();
        listensock_.Bind(port_);
        listensock_.Listen();

        for (int i = 0; i < N; i++)
            fdarray_[i] = defaultfd;
    }
    void Accepter()
    {
        std::string clientip;
        uint16_t clientport;
        int sock = listensock_.Accept(&clientip, &clientport);
        if (sock < 0)
            return;
        logMessage(Debug, "[%s:%d], sock: %d", clientip.c_str(), clientport, sock);
        int pos = 1;
        for (; pos < N; pos++)
        {
            if (fdarray_[pos] == defaultfd)
                break;
        }
        if (pos >= N)
        {
            close(sock);
            logMessage(Warning, "sockfd array[] full");
        }
        else
        {
            fdarray_[pos] = sock;
        }
    }

    void HandlerEvent(fd_set &rfds)
    {
        for (int i = 0; i < N; i++)
        {
            if (fdarray_[i] == defaultfd)
                continue;

            if ((fdarray_[i] == listensock_.Fd()) && FD_ISSET(listensock_.Fd(), &rfds))
            {
                Accepter();
            }
            else if ((fdarray_[i] != listensock_.Fd()) && FD_ISSET(fdarray_[i], &rfds))
            {
                int fd = fdarray_[i];
                char buffer[1024];
                ssize_t s = recv(fd, buffer, sizeof(buffer) - 1, 0);
                if (s > 0)
                {
                    buffer[s-1] = 0;
                    std::cout << "client# " << buffer << std::endl;
                    std::string echo = buffer;
                    echo += " [select server echo]";
                    send(fd, echo.c_str(), echo.size(), 0);
                }
                else
                {
                    if (s == 0)
                        logMessage(Info, "client quit ..., fdarray_[i] -> defaultfd: %d->%d", fd, defaultfd);
                    else
                        logMessage(Warning, "recv error, client quit ..., fdarray_[i] -> defaultfd: %d->%d", fd, defaultfd);
                    close(fdarray_[i]);
                    fdarray_[i] = defaultfd;
                }
            }
        }
    }
    void Start()
    {
        fdarray_[0] = listensock_.Fd();
        while (true)
        {
            fd_set rfds;
            FD_ZERO(&rfds);
            int maxfd = fdarray_[0];
            for (int i = 0; i < N; i++)
            {
                if (fdarray_[i] == defaultfd)
                    continue;
                // 合法fd
                FD_SET(fdarray_[i], &rfds);
                if (maxfd < fdarray_[i])
                    maxfd = fdarray_[i];
            }

            int n = select(maxfd + 1, &rfds, nullptr, nullptr, nullptr);
            switch (n)
            {
            case 0:
                logMessage(Debug, "timeout, %d: %s", errno, strerror(errno));
                break;
            case -1:
                logMessage(Warning, "%d: %s", errno, strerror(errno));
                break;
            default:
                logMessage(Debug, "有一个就绪事件发生了: %d", n);
                HandlerEvent(rfds);
                DebugPrint();
                break;
            }
        }
    }
    void DebugPrint()
    {
        std::cout << "fdarray[]: ";
        for (int i = 0; i < N; i++)
        {
            if (fdarray_[i] == defaultfd)
                continue;
            std::cout << fdarray_[i] << " ";
        }
        std::cout << "\n";
    }
    ~PollServer()
    {
        listensock_.Close();
    }

private:
    uint16_t port_;
    Sock listensock_;
    type_t fdarray_[N];
};

改一下数组类型

typedef struct pollfd type_t;
type_t* fdarray_;

在构造函数那里初始化为nullptr,然后在初始化函数初始化

const static int gport = 8888;
const static int N = 4096;
const static short defaultevent = 0;

typedef struct pollfd type_t;

class PollServer
{
public:
    PollServer(uint16_t port = gport) : port_(port), fdarray_(nullptr)
    {
    }
    void InitServer()
    {
        listensock_.Socket();
        listensock_.Bind(port_);
        listensock_.Listen();
        fdarray_ = new type_t[N];
        for (int i = 0; i < N; i++)
        {
            fdarray_[i].fd = defaultfd;
            fdarray_[i].events = defaultevent;
            fdarray_[i].revents = defaultevent;
        }
    }

    ~PollServer()
    {
        listensock_.Close();
        if(fdarray_) delete []fdarray_;//判断不为空再delete
    }

start函数里这些不再需要

            fd_set rfds;
            FD_ZERO(&rfds);
            int maxfd = fdarray_[0].fd;
            for (int i = 0; i < N; i++)
            {
                if (fdarray_[i] == defaultfd)
                    continue;
                // 合法fd
                FD_SET(fdarray_[i], &rfds);
                if (maxfd < fdarray_[i])
                    maxfd = fdarray_[i];
            }

    void Start()
    {
        fdarray_[0].fd = listensock_.Fd();
        fdarray_[0].events = POLLIN;
        while (true)
        {
            int timeout = 1000;
            int n = poll(fdarray_, N, timeout);
            switch (n)
            {
            case 0:
                logMessage(Debug, "timeout, %d: %s", errno, strerror(errno));
                break;
            case -1:
                logMessage(Warning, "%d: %s", errno, strerror(errno));
                break;
            default:
                logMessage(Debug, "有一个就绪事件发生了: %d", n);
                HandlerEvent(rfds);
                DebugPrint();
                break;
            }
        }
    }

poll函数那里,第二个参数可以不传N,把要管理的fd都放到fdarray_最左侧排起来,第二个参数就可以只看这几个fd的个数。

其它函数更改一下

    void Accepter()
    {
        std::string clientip;
        uint16_t clientport;
        int sock = listensock_.Accept(&clientip, &clientport);
        if (sock < 0)
            return;
        logMessage(Debug, "[%s:%d], sock: %d", clientip.c_str(), clientport, sock);
        int pos = 1;
        for (; pos < N; pos++)
        {
            if (fdarray_[pos].fd == defaultfd)
                break;
        }
        if (pos >= N)
        {
            //可以先动态扩容,扩容失败再close
            close(sock);
            logMessage(Warning, "sockfd array[] full");
        }
        else
        {
            fdarray_[pos].fd = sock;
            fdarray_[pos].events = POLLIN;//可以设置成POLLIN / POLLOUT,也就是读写事件都关心
            fdarray_[pos].revents = defaultevent;
        }
    }

    void HandlerEvent()
    {
        for (int i = 0; i < N; i++)
        {
            int fd = fdarray_[i].fd;
            short revent = fdarray_[i].events;
            if (fd == defaultfd)
                continue;

            if ((fd == listensock_.Fd()) && (revent & POLLIN))//fd是需要的fd且读数据就绪
            {
                Accepter();
            }
            else if ((fd != listensock_.Fd()) && (revent & POLLIN))//fd不是我们要的,但读数据就绪
            {
                int fd = fdarray_[i].fd;
                char buffer[1024];
                ssize_t s = recv(fd, buffer, sizeof(buffer) - 1, 0);
                if (s > 0)
                {
                    buffer[s-1] = 0;
                    std::cout << "client# " << buffer << std::endl;
                    std::string echo = buffer;
                    echo += " [select server echo]";
                    send(fd, echo.c_str(), echo.size(), 0);
                }
                else
                {
                    if (s == 0)
                        logMessage(Info, "client quit ..., fdarray_[i] -> defaultfd: %d->%d", fd, defaultfd);
                    else
                        logMessage(Warning, "recv error, client quit ..., fdarray_[i] -> defaultfd: %d->%d", fd, defaultfd);
                    close(fd);
                    fdarray_[i].fd = defaultfd;
                    fdarray_[i].events = defaultevent;
                    fdarray_[i].revents = defaultevent;
                }
            }
        }
    }

makefile

pollserver:main.cc
	g++ -o $@ $^ -std=c++11
.PHONY:clean
clean:
	rm -f pollserver

main.cc

#include "PollServer.hpp"
#include <memory>

int main()
{
    //fd_set fd;
    //std::cout << sizeof(fd) << std::endl;
    std::unique_ptr<PollServer> svr(new PollServer());
    svr->InitServer();
    svr->Start();
    return 0;
}

timeout改成-1就是阻塞了。现在这个服务器支持读,如果要支持写,在HandlerEvent函数里

              if (s > 0)
                {
                    buffer[s-1] = 0;
                    std::cout << "client# " << buffer << std::endl;
                    fdarray_[i].events |= POLLOUT;//也关心写事件
                    std::string echo = buffer;
                    echo += " [select server echo]";
                    send(fd, echo.c_str(), echo.size(), 0);
                }

3、特点

poll虽然相对于select简单了好多,不过poll也是以数组形式传多个fd,让操作系统去遍历所有fd,所以对于底层的消耗和select一样。但poll解决了fd上限少的问题,数组多大是用户决定的,而select自己就决定了一个fd_set,让用户只得用fd_set。当用户定的数组太大时,操作系统去遍历,效率也低。所以poll对于select,虽然看上去简单了一些,但放到实际中,文件逐渐增多,两者都不怎么样,不过poll写起来更简单。

select可以跨平台,poll不行。


本篇gitee

结束。

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

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

相关文章

Linux系统SSH远程管理服务

目录 一、SSH协议是什么&#xff1f; 1、SSH协议的定义&#xff1a; 2、SSH协议的优点 3、SSH的客户端与服务端 4、SSH的原理 4.1公钥首次连接原理 4.2ssh远程登录 4.3使用简单的SSH远程登录 二、OpenSSH服务器 1、OpenSSH简介 2、配置Openssh服务端 3、SSH服务的最…

Unity中URP中的光照简介

文章目录 前言URP下的光照在Unity中的设置1、主灯设置2、额外灯设置3、反射光设置 前言 我们在这篇文章开始了解URP下的光照。 URP下的光照在Unity中的设置 1、主灯设置 主灯可以选择 禁用 或 逐像素 光照 当选择逐像素光照的主灯后 Cast Shadows&#xff1a;可以选择开启 或…

CSS 流光发光按钮

<template><view class="content"><view class="a"><text></text><text></text><text></text><text></text>发光按钮</view></view></template><script></…

HTML标签(一)

目录 HTML语法规范 基本语法概述 标签关系 HTML基本结构标签 第一个HTML网页 开发工具 VSCode的使用&#xff1a; VScode工具生成骨架标签新增代码 文档类型声明标签 lang语言种类 字符集 总结 HTML常用标签 标签语义 标题标签 段落标签 换行标签 文本格式化…

如何公网远程访问Linux AMH服务器管理面板【内网穿透】

⛳️ 推荐 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站。 文章目录 ⛳️ 推荐1. Linux 安装AMH 面板2. 本地访问AMH 面板3. Linux安装Cpolar4. 配置AMH面板公网地址5. 远程访问AMH面板6…

【UEFI基础】EDK网络框架(ARP)

ARP ARP协议说明 从这里开始涉及到的网络协议都是比较通用的了&#xff0c;在一般的TCP/IP四层模型中都能够看到这些内容&#xff0c;不过这里主要介绍的还是其在BIOS下的实现&#xff0c;但是在此之前还是需要先说明ARP的作用。 ARP的全称是Address Resolution Protocol&am…

AI-数学-高中-4.函数表达式特性-要变一起变

求f(x):换元法&#xff1a;左边代换时&#xff0c;右边也要同时替换&#xff0c;原作者视频&#xff1a;函数】1引导课&#xff1a;高中为什么用f(x)_哔哩哔哩_bilibili 1.什么是函数&#xff1a;给定任意一个x&#xff0c;都有唯一确定的y与之对应&#xff0c;这种x与y的关系就…

如何提高匹配的精确度(多次学习)

我们工业自动化中&#xff0c;视觉软件匹配&#xff0c;都是学习一次&#xff0c;比如找到轮廓&#xff0c;旋转360度&#xff0c;也就是有360个轮廓&#xff0c;然后到图像中去找任意角度的目标。 这样的学习并不能一而概括全。 所以&#xff0c;我借鉴ai的方法&#xff0c;…

【计算机组成原理】指令流水线的三种冒险情况(Hazards)

冒险 在计算机架构中&#xff0c;流水线冒险是指在指令流水线的执行过程中由于数据相关性或控制相关性而导致的一种性能问题。指令流水线是将指令执行过程划分为多个阶段&#xff0c;这样可以同时处理多条指令&#xff0c;从而提高指令执行的效率。然而&#xff0c;流水线执行…

阳光保险选择OceanBase稳定运行超700天

阳光保险集团成立于 2005 年 7 月&#xff0c;旗下拥有财产保险、人寿保险、信用保证保险、资产管理等多家专业子公司&#xff0c;是全球市场化企业中成长最快的集团公司之一&#xff0c;目前位列中国保险行业前八。随着数字化升级趋势的不断加速&#xff0c;很多企业产生将软硬…

Kali Linux——aircrack-ng无线教程

目录 一、准备 二、案例 1、连接usb无线网卡 2、查看网卡信息 3、开启网卡监听 4、扫描wifi信号 5、抓取握手包 6、强制断开连接 7、破解握手包 三、预防 一、准备 1、usb无线网卡&#xff08;笔记本也是需要用到&#xff09; 2、密码字典&#xff08;Kali 系统自带…

微信小程序 全局配置||微信小程序 页面配置||微信小程序 sitemap配置

全局配置 小程序根目录下的 app.json 文件用来对微信小程序进行全局配置&#xff0c;决定页面文件的路径、窗口表现、设置网络超时时间、设置多 tab 等。 以下是一个包含了部分常用配置选项的 app.json &#xff1a; {"pages": ["pages/index/index",&q…

软件设计考试相关信息

简单介绍 软考是国家人力资源和社会保障部、工业和信息化部联合组织实施的国家级考试&#xff0c;参加计算机软件资格考试并取得相应级别的资格证书&#xff0c;是各用人单位聘用计算机技术与软件专业工程师系列职务的前提。计算机软件资格考试&#xff0c;与会计、经济师、税…

2024年广东省安全员C证第四批(专职安全生产管理人员)证模拟考试题库及广东省安全员C证第四批(专职安全生产管理人员)理论考试试题

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2024年广东省安全员C证第四批&#xff08;专职安全生产管理人员&#xff09;证模拟考试题库及广东省安全员C证第四批&#xff08;专职安全生产管理人员&#xff09;理论考试试题是由安全生产模拟考试一点通提供&#…

科大讯飞星火大模型接入API js 部分 接口 安装注意事项

下载以下链接例子运行程序 https://xfyun-doc.xfyun.cn/static%2F16968175055332330%2Fspark_js_demo.zip 官网给的说明 准备 1.在demo中填写APPID、APISecret、APIKey&#xff0c;可到控制台-我的应用-大模型页面获取 2.安装nodejs 本地运行 1.打开cmd&#xff0c;进入dem…

CSS 下载进度条

<template><view class=btn>下载中</view></template><script></script><style>/* 设置整个页面的样式 */body {width: 100vw; /* 页面宽度为视口宽度 */background: #000000; /* 背景颜色为白色 */display: flex; /* 使用 flex…

浅析五种 React 组件设计模式

作为一名 React 开发者&#xff0c;你可能会面临下面几个问题&#xff1a; 如何构建一个高复用度性的组件&#xff0c;使其适应不同的业务场景&#xff1f;如何构建一个具有简单 API的组件&#xff0c;使其易于使用&#xff1f;如何构建一个在 UI 和功能方面具有可扩展性的组件…

四大会计假设

目录 一. 会计主体假设二. 持续经营假设三. 会计期间假设四. 货币计量假设 \quad \quad 一. 会计主体假设 \quad 会计主体: 会计工作为其服务的特定单位或组织。 会计主体的定义 1.具有一定数量的资金。 2.进行独立的生产经营或其他活动。 3.实行独立核算。 \quad 会计主体假设…

【亲测可行】如何申请并登录腾讯云免费服务器

腾讯云免费服务器申请入口 https://curl.qcloud.com/FJhqoVDP 免费服务器可选轻量应用服务器和云服务器CVM&#xff0c;轻量配置可选2核2G3M、2核8G7M和4核8G12M&#xff0c;CVM云服务器可选2核2G3M和2核4G3M配置&#xff0c;腾讯云百科txybk.com分享2024年最新腾讯云免费服务器…

5V高细分步进电机驱动芯片选型分析

单通道5V高细分步进电机GC6139 GC6106 GC6107 GC6119 GC6151 GC6236 GC8558 它们应用在摇头机&#xff0c;X,Y控制&#xff0c;聚焦控制等产品上。其中GC8558为24V H 桥驱动&#xff0c;大电流&#xff0c;具有短地短电源保护&#xff0c;限流保护等功能。