【高性能服务器】poll模型

news2024/9/21 8:03:40

 🔥博客主页: 我要成为C++领域大神
🎥系列专栏:【C++核心编程】 【计算机网络】 【Linux编程】 【操作系统】
❤️感谢大家点赞👍收藏⭐评论✍️

本博客致力于知识分享,与更多的人进行学习交流

poll模型

关于select模型可以查看我这期文章【高性能服务器】select模型

实现原理

poll 不再用 BitsMap 来存储所关注的文件描述符,取而代之用动态数组,以链表形式来组织,突破了 select 的文件描述符个数限制,当然还会受到系统文件描述符限制。但是 poll 和 select 并没有太大的本质区别,都是使用「线性结构」存储进程关注的 Socket 集合,因此都需要遍历文件描述符集合来找到可读或可写的 Socket,时间复杂度为 O(n),而且也需要在用户态与内核态之间拷贝文件描述符集合,这种方式随着并发数上来,性能的损耗会呈指数级增长。

核心接口

struct pollfd listen_array[10000]; 自定义长度

struct pollfd node结构体

struct pollfd {
               int   fd;         /* file descriptor */
               short events;     /* requested events */
               short revents;    /* returned events */
           };

node.fd=sockfd;  //存储监听的socket,取消监听设置-1
node.events=POLLIN |POLLOUT | POLLERR;  //设置要监听的socket事件
node.revents=POLLIN //当socket就绪后,传出就绪事件,后续使用revents判断socket就绪

int poll(struct pollfd *fds, nfds_t nfds, int timeout)

timeout -1阻塞 >0定时阻塞 0非阻塞

代码实现

PollSock.h

#ifndef _POLLSOCK_H_
#define _POLLSOCK_H_

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


int SOCKET(int domain, int type, int protocol);
int BIND(int sockfd, struct sockaddr* addr, socklen_t addrlen);
ssize_t RECV(int sockfd, void* buf, size_t len, int flags);
ssize_t SEND(int sockfd, void* buf, size_t len, int flags);
int CONNECT(int sockfd, struct sockaddr* addr, socklen_t addrlen);
int ACCEPT(int sockfd, struct sockaddr* addr, socklen_t* addrlen);
int LISTEN(int sockfd, int backlog);
char* FGETS(char* s, int size, FILE* stream);
int POLL(struct pollfd *fds, nfds_t nfds, int timeout);
int socket_init();
int return_response(int clientfd, const char* clientip);

// 全局变量声明
char recv_buf[1024];
char time_buf[64];
int serverFd, clientFd;
struct sockaddr_in clientAddr;
struct pollfd client_array[10000];
int ready;
socklen_t addrlen;
char clientip[16];
time_t tp;
ssize_t recvlen;
int toupper_flag;
#define SHUTDOWN 1
#endif

PollSock.c

#include "PollSock.h"

int SOCKET(int domain, int type, int protocol) {
    int reval = socket(domain, type, protocol);
    if (reval == -1) {
        perror("socket call failed");
        exit(0);
    }
    return reval;
}

int BIND(int sockfd, struct sockaddr* addr, socklen_t addrlen) {
    int reval = bind(sockfd, addr, addrlen);
    if (reval == -1) {
        perror("bind call failed");
        exit(0);
    }
    return reval;
}

ssize_t RECV(int sockfd, void* buf, size_t len, int flags) {
    ssize_t reval;
    reval = recv(sockfd, buf, len, flags);
    return reval;
}

ssize_t SEND(int sockfd, void* buf, size_t len, int flags) {
    ssize_t reval;
    reval = send(sockfd, buf, len, flags);
    if (reval == -1)
        perror("send call failed");
    return reval;
}

int CONNECT(int sockfd, struct sockaddr* addr, socklen_t addrlen) {
    int reval = connect(sockfd, addr, addrlen);
    if (reval == -1) {
        perror("connect call failed");
        exit(0);
    }
    return reval;
}

int ACCEPT(int sockfd, struct sockaddr* addr, socklen_t* addrlen) {
    int reval = accept(sockfd, addr, addrlen);
    if (reval == -1) {
        perror("accept call failed");
        exit(0);
    }
    return reval;
}

int LISTEN(int sockfd, int backlog) {
    int reval = listen(sockfd, backlog);
    if (reval == -1) {
        perror("listen call failed");
        exit(0);
    }
    return reval;
}

char* FGETS(char* s, int size, FILE* stream) {
    char* str;
    if ((str = fgets(s, size, stream)) != NULL) {
        return str;
    } else {
        perror("fgets call failed");
        exit(0);
    }
}

int socket_init() {
    struct sockaddr_in sockAddr;
    sockAddr.sin_family = AF_INET;
    sockAddr.sin_addr.s_addr = htonl(INADDR_ANY);
    sockAddr.sin_port = htons(8080);
    int sock_fd = SOCKET(AF_INET, SOCK_STREAM, 0);
    BIND(sock_fd, (struct sockaddr*)&sockAddr, sizeof(sockAddr));
    LISTEN(sock_fd, 5);
    return sock_fd;
}

int return_response(int clientfd, const char* clientip) {
    char response[1024];
    bzero(response, sizeof(response));
    sprintf(response, "Hi [%s],This is TCP Server Working...\n", clientip);
    SEND(clientfd, response, sizeof(response), 0);
}

int POLL(struct pollfd *fds, nfds_t nfds, int timeout){
    int reval=poll(fds,nfds,timeout);
    if(reval==-1)
    {
        perror("poll call failed");
        exit(0);
    }
    return reval;
}

PollServer.c

#include "PollSock.h"

int main() {
    bzero(recv_buf, sizeof(recv_buf));
    bzero(time_buf, sizeof(time_buf));
    bzero(clientip,sizeof(clientip));
    serverFd = socket_init();
    int i;
    for (i = 0; i < 10000; ++i) {
        client_array[i].fd = -1;//初始化poll数组
        client_array[i].events=POLLIN;//设置所有socket默认为监听读
    }
    client_array[0].fd=serverFd;
    printf("Test poll Server is Running...\n");

    while (SHUTDOWN) {
        ready = POLL(client_array, 10000, -1); //阻塞监听
        while (ready) { // 辨别就绪
            if (client_array[0].revents==POLLIN) {
                addrlen = sizeof(clientAddr);
                clientFd =ACCEPT(serverFd, (struct sockaddr*)&clientAddr, &addrlen);
                inet_ntop(AF_INET, &clientAddr.sin_addr.s_addr, clientip, 16);
                printf("Listen Server Socket Successfully Call Accept, Client IP [%s], PORT[%d]\n",clientip, ntohs(clientAddr.sin_port));
                return_response(clientFd, clientip);
                for (i = 1; i < 100000; ++i)
                    if (client_array[i].fd == -1) {
                        client_array[i].fd = clientFd;
                        break;
                    }
            } else {
                // 仅处理一次客户端请求,单进程不允许客户端持续占用
                for (i = 1; i < 10000; ++i)
                    {if (client_array[i].fd != -1)
                        if (client_array[i].revents==POLLIN)
                        {
                            if ((recvlen = RECV(client_array[i].fd, recv_buf, sizeof(recv_buf), 0)) >0) 
                             { // 处理客户端业务
                                    printf("Client Say:%s\n", recv_buf);
                                    if (strcmp(recv_buf, "localtime") == 0) {
                                        tp = time(NULL); // 获取时间种子
                                        ctime_r(&tp, time_buf);
                                        time_buf[strcspn(time_buf, "\n")] = '\0';
                                        printf("[%s]Response SysTime Successfully!\n", clientip);
                                        SEND(client_array[i].fd, time_buf, strlen(time_buf) + 1, 0);
                                        bzero(time_buf, sizeof(time_buf));
                                    } else {
                                        toupper_flag = 0;
                                        while (recvlen > toupper_flag) {
                                            recv_buf[toupper_flag] = toupper(recv_buf[toupper_flag]);
                                            ++toupper_flag;
                                        }
                                        printf("[%s]Response Toupper Successfully!\n", clientip);
                                        SEND(client_array[i].fd, recv_buf, recvlen, 0);
                                        bzero(recv_buf, sizeof(recv_buf));
                                    }
                                } else if (recvlen == 0) {
                                    close(client_array[i].fd);
                                    client_array[i].fd = -1;
                                    printf("Client is Exiting, Delete Listen Item.\n");
                                }
                                break;
                            }
                    }
            }
            ready--;
        }
    }

    printf("Server is Over\n");
    close(serverFd);
    return 0;
}

Client.c

#include "MySock.c"


//客户端源码编写,连接服务器成功,服务器反馈信息

#define _IP "39.102.213.44"
#define _PORT 8080
int main()
{
    struct sockaddr_in ServerAddr;
    bzero(&ServerAddr,sizeof(ServerAddr));
    ServerAddr.sin_family=AF_INET;
	ServerAddr.sin_port=htons(_PORT);
    inet_pton(AF_INET,_IP,&ServerAddr.sin_addr.s_addr);
    
    int Myfd=SOCKET(AF_INET,SOCK_STREAM,0);
    //看需求决定是否要绑定
    char Response[1024];//存放服务端反馈信息
    ssize_t recvlen;
    bzero(Response,sizeof(Response));
    char sendbuf[1024];
    
    if((CONNECT(Myfd,(struct sockaddr *)&ServerAddr,sizeof(ServerAddr)))==0)
    {
    while(1)
    {    
     if((recvlen=RECV(Myfd,Response,sizeof(Response),0))>0)
     {
         printf("%s\n",Response);
     }
    
    printf("Please Type Some text:");//读取标准输入发送给服务端
    FGETS(sendbuf,sizeof(sendbuf),stdin);    
    sendbuf[strcspn(sendbuf,"\n")]='\0';
    SEND(Myfd,sendbuf,sizeof(sendbuf),0);
    }
    }
    close(Myfd);
    printf("Client is Over\n");
    return 0;
}
运行结果

这里的pollfd结构体数组虽然设置的大小是10000,但是当打开的客户端超过1023个(还有一个是服务端socket)后,服务端就会崩溃。因为系统默认一个进程可以打开的文件描述符是1024个。通过ulimit -a可以查看

通过cat /proc/sys/fs/file-max命令可以查看系统可以使用的描述符最大值

可以通过ulimit -n临时修改一个进程默认打开文件描述符最大值(注意不能超过系统限制),仅在当前终端下生效,新的终端失效

要想在所有情况都生效,可以修改系统配置。sudo vi /etc/security/limits.conf查看系统限制配置,修改软限制和硬限制。

保存退出,重启生效

poll模型优缺点

优点:

1.poll可以监听的事件数量更丰富

2.poll可以对不同的socket进行不同的监听设置,设置方式更灵活

3.进行了传入传出分离设置,events传入监听的事件,revents传出就绪事件

缺点:

1.兼容性差,Windows平台不支持,Linux少数不支持

2.轮询问题, 随着轮询数量的增长,IO处理性能呈线性下降

3.poll只返回就绪的数量,没有反馈就绪的socket,需要用户自行遍历查找,开销较大

4.select多轮使用会出现大量重复的拷贝开销和挂载监听开销

5.仅支持毫秒级别的定时阻塞监

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

算法-计数质数

题目&#xff1a; 给定整数 n &#xff0c;返回 所有小于非负整数 n 的质数的数量 。 思路&#xff1a; 使用埃式筛法 当n大于等于2时&#xff0c;如果当前遍历的数 i 是质数&#xff0c;那么从 i*i 开始&#xff0c;直到 n 为止&#xff0c;把 i 的倍数都标记为合数 代码&a…

一个关于STM32的DAC输出的遗忘点

众所周知熟练使用HAL库可以帮你解决不少stm32的开发难题&#xff0c;但是是谁让你陷入了这些难题&#xff0c;别问。 如上图所示&#xff0c;正常初始化这个模块后生成代码如下图所示&#xff1b; * DAC init function */ void MX_DAC_Init(void) {/* USER CODE BEGIN DAC_Ini…

【LabVIEW作业篇 - 3】:数组相加、for循环创建二位数组、数组练习(求最大最小值、平均值、中位数、提取范围内的数据、排序)

文章目录 数组相加for循环实现直接使用加函数 for循环创建二位数组数组练习 数组相加 要求&#xff1a;用两种方法实现两个数组相加 for循环实现 在前面板中分别创建两个数值类型的一维数组&#xff0c;并设置相应的值&#xff0c;然后在程序框图中创建一个for循环&#xff…

数据结构(Java):力扣牛客 二叉树面试OJ题(一)

&#x1f449; ​​​​​​目录 &#x1f448; 1、题一&#xff1a;检查两棵树是否相同 1.1 思路分析 1.2 代码 2、题二&#xff1a;另一棵树的子树 2.1 思路分析 2.2 代码 3、题三&#xff1a;翻转二叉树 3.1 思路分析 3.2 代码 4、题四&#xff1a;判断树是否对称 …

小模型大突破!神经网络透视空间异质性,准确描述复杂地理现象

为推进 AI4S 的普适化&#xff0c;降低学术机构科研成果的传播壁垒&#xff0c;为更多行业学者、科技爱好者及产业单位提供交流平台&#xff0c;HyperAI超神经策划了「Meet AI4S」系列直播栏目&#xff0c; 邀请深耕 AI for Science 领域的科研人员或相关单位&#xff0c;以视频…

新时代多目标优化【数学建模】领域的极致探索——数学规划模型

目录 例1 1.问题重述 2.基本模型 变量定义&#xff1a; 目标函数&#xff1a; 约束条件&#xff1a; 3.模型分析与假设 4.模型求解 5.LINGO代码实现 6.结果解释 ​编辑 7.敏感性分析 8.结果解释 例2 奶制品的销售计划 1.问题重述 ​编辑 2.基本模型 3.模…

北京邮电大学,中央空调的分户计费系统

北京邮电大学 中央空调如何公平、公正、合理的收取费用&#xff0c;一直都是各建筑管理者的首要问题。北京邮电大学也面临着能源分配不公&#xff0c;学校管理者空调收费管理困难等问题。根据学校的具体情况&#xff0c;拓森为其制定了一套中央空调管理运营方案—无线中央空调…

jupyter学习笔记

简介 Jupyter Notebook是一个Web应用程序&#xff0c;它可以在网页页面中直接编写代码和运行代码&#xff0c;代码的运行结果也会直接在代码块下显示。 安装使用 前提&#xff1a;必须安装python 先升级pip至最新版本 pip3 install --upgrade pip安装jupyter notebook pi…

springboot的简单应用

Mvc与三层架构 创建Spring项目 勾选web和mabais框架 配置yml文件&#xff0c;这里创建spring项目默认生成的是propertise文件&#xff0c;但是properties文件的格式看起来没有yml文件一目了然。yml文件配置数据库还有映射mapper层的xml文件以及设置日志级别&#xff0c;比如map…

刚起步的家庭海外仓:涉及到的全部业务优化流程

对于家庭海外仓来说&#xff0c;最难的阶段应该就是刚起步的时候。对业务流程不熟悉&#xff0c;也没有客户积累&#xff0c;本身的预算又十分有限。 在这个情况下应该注意什么&#xff0c;怎样才能顺利的开展业务&#xff1f;今天我们就针对这个问题详细的梳理了一下家庭海外…

尚品汇-(二十一)

目录&#xff1a; &#xff08;1&#xff09;使用redis实现分布式锁 &#xff08;2&#xff09;优化之设置锁的过期时间 &#xff08;3.&#xff09;优化之UUID防误删 &#xff08;4&#xff09;优化之LUA脚本保证删除的原子性 &#xff08;1&#xff09;使用redis实现分布…

go语言编程 小试牛刀 goroutine和reflect知识点

&#xff08;一&#xff09;goroutine package _caseimport "fmt"// sum 函数计算整数切片 values 的总和&#xff0c;并将结果发送到 resultChan 通道中 func sum(values []int, resultChan chan int) {sum : 0for _, value : range values {sum value}resultChan…

HarmonyOS NEXT学习——@Styles、@Extend、stateStyles

Styles装饰器 定义组件重用样式 仅支持通用属性和通用事件不支持参数可以定义全局和组件内使用&#xff0c;全局使用需要加function // 全局 Styles function functionName() { ... }// 在组件内 Component struct FancyUse {Styles fancy() {.height(100)} }组件内Styles的优…

智能一体式闸门在灌区中的应用

在现代化的农业灌溉领域&#xff0c;智能一体式闸门作为一种集自动化、智能化、高效能于一体的先进设备&#xff0c;正逐渐在灌区管理中发挥着重要作用。 灌区是农业生产的重要基地&#xff0c;其水资源的管理和利用直接关系到农作物的生长和产量。然而&#xff0c;传统的闸门管…

旋转中的图片视觉差效果

Hello&#xff0c;亲爱的宝子们&#xff1f;最近我一个前端架构师却临时顶替产品经理的工作&#xff0c;导致最近一周实在太忙了&#xff0c;都没有来得及更新文章。在这里想大家道歉了&#xff01;也想厚颜无耻的问问大家想我了吗&#xff1f;(●◡●) 今天给大家带来一个非常…

Vue 使用 Element UI 组件库

https://andi.cn/page/621589.html

RAG介绍

一&#xff0c;RAG概述 RAG&#xff08;Retrieval-Augmented Generation&#xff0c;检索增强生成&#xff09;是一种结合了信息检索与生成任务的技术&#xff0c;它通过将外部知识库与大模型的生成能力相结合&#xff0c;提高了生成内容的准确性和丰富性。以下是关于RAG应用的…

【产品那些事】固件安全-关于OTA升级包分析

文章目录 前言什么是OTA?升级包(固件)的类型和架构案例tp-link路由器升级包怎么解包分析?binwalk安装及使用ubi_reader安装及使用unsquashfs安装及使用某车企OTA升级包通用Android OTA解包相关分区第二层解包前言 什么是OTA? OTA(Over-the-Air)是一种通过无线通信网络(…

go的Mutex实现原理及演进

下面的这个是对于昨天晚上读的几篇关于go中锁的文章知识点的总结 文章目录 1. 引言1.1 并发编程的挑战1.2 Mutex 的角色1.3 Mutex 设计的演进1.4 技术追求的美妙 引言部分详细解释引言部分注意点引言部分流程图 2. Mutex 架构演进2.1 初版 Mutex 设计2.2 性能优化 - 给新人机会…

【ffmpeg】一篇文章搞定YUV

文章目录 前言YUV是什么&#xff1f;YUV的用途YUV采样格式采样格式是什么YUV采样格式有哪些YUV采样格式的区别 YUV与RGBRGB 颜色空间YUV 颜色空间RGB 与 YUV 的比较RGB 转 YUV 公式YUV 转 RGB 公式注意事项 YVU数据计算通用公式4:4:4 采样格式4:2:2 采样格式4:2:0 采样格式实例…