多路转接之poll

多路转接之poll

  • 一、关于poll
    • 认识poll
    • 基于poll实现的服务器的原理
  • 二、基于poll实现的服务器
    • main.cpp
    • pollServer.hpp
    • sock.hpp
    • Log.hpp
    • public.hpp

一、关于poll

认识poll

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

poll的返回值ret:
ret<0,poll调用失败。
ret=0,poll超时等待,没有文件描述符的事件就绪。
ret>0,有ret个文件描述符现在事件就绪。
poll的第一个选项fds是一个动态数组,数组中每个元素是struct pollfd的结构体。
poll的第二个选项是动态数组的大小。
poll的最后一个选项timeout:
timeout>0:在timeout毫秒内没有文件描述符的事件就绪,就一直阻塞,直到timeout返回。
timeout=0:select非阻塞。
timeout<0:select阻塞等待。
用户想要内核监视哪个文件描述符的哪个事件,只需要定义struct pollfd结构体,将将文件描述符和事件传给结构体中的fd和events,再将结构体加入fds数组中,让poll监视数组中的文件描述符事件。
内核通过数组中struct pollfd结构,告诉用户哪个文件描述符的哪个事件就绪,在结构体中的fd和revents代表,文件描述符fd的revnents事件就绪。
两条命令:
uname -a;查看linux操作系统信息。
ulimit -a;查看进程可以使用的资源。
event和revent的取值:
在这里插入图片描述

基于poll实现的服务器的原理

维护一个struct pollfd结构体的数组,存储需要监视的文件描述符的事件。
将数组的第一个结构体来设置成监听套接字的读事件。
进行poll系统调用,用户将想要监视的文件描述符事件告诉操作系统内核。
若有文件描述符事件就绪poll就返回大于0的数。
用户通过遍历struct pollfd结构体数组,找到revent不为0的结构体,对就绪文件描述符事件做处理。

二、基于poll实现的服务器

main.cpp

#include "pollServer.hpp"
#include <memory>
using namespace std;
using namespace poll_ns;
std::string handle(const std::string& request){return request;
}
static void usage(string proc){cerr<<proc<<" need a port!!!!"<<endl;
}
int main(int argc,char* argv[]){if(argc!=2){usage(argv[0]);exit(1);}uint16_t port=atoi(argv[1]);//实现一个基于poll的只处理读事件的Serverunique_ptr<pollServer> svr(new pollServer(handle,port));svr->init();svr->start();return 0;
}

pollServer.hpp

#pragma once
#include <iostream>
#include "sock.hpp"
#include "public.hpp"
#include <errno.h>
#include <cstring>
#include <sys/select.h>
#include <functional>
#include <poll.h>
using namespace std;
namespace poll_ns{class pollServer{private:static const uint16_t defaultPort=8080;static const int num=2048;static const int defaultfd=-1;using func_t=std::function<std::string(const std::string)>;public://打印有效文件描述符void print(){cout<<"socket list:"<<endl;for(int i=0;i<num;++i){if(_rfds[i].fd!=defaultfd)cout<<_rfds[i].fd<<" ";}cout<<endl;}pollServer(func_t func,uint16_t port=defaultPort):_func(func),_port(port),_listenSockfd(-1),_rfds(nullptr){}void init(){_listenSockfd=Sock::Socket();Sock::Bind(_listenSockfd,_port);Sock::Listen(_listenSockfd);_rfds=new struct pollfd[num];for(int i=0;i<num;++i){resetItem(i);}_rfds[0].fd=_listenSockfd;_rfds[0].events=POLLIN;}//监听套接字的读事件就绪void accepter(){//listenSockfd监听套接字的读事件就绪,也就是监听套接字下有客户端申请新连接string clientIp;uint16_t clientPort;int sockfd=Sock::Accept(_listenSockfd,&clientIp,&clientPort);if(sockfd==-1)return;log(NORMAL,"accept a new link from %s-%d,sockfd=%d",clientIp.c_str(),clientPort,sockfd);int i=0;for(;i<num;++i){if(_rfds[i].fd==defaultfd)break;}if(i==num){log(WARNING,"fdarray is full!!! need close fd");close(sockfd);}else{//将连接套接字文件描述符需要监视的事件加入struct pollfd数组中//下一次poll就可监视连接套接字的状态的了_rfds[i].fd=sockfd;_rfds[i].events=POLLIN;_rfds[i].revents=0;}print();}void resetItem(int i){_rfds[i].fd=defaultfd;_rfds[i].events=0;_rfds[i].revents=0;}//连接套接字的读事件就绪void recver(int pos){char buffer[1024];//不能保证读完一个完整的应用层报文ssize_t n=recv(_rfds[pos].fd,buffer,sizeof(buffer)-1,0);if(n>0){buffer[n]=0;std::cout<<"read a meassge:"<<buffer<<std::endl;}//连接被关闭else if(n==0){close(_rfds[pos].fd);resetItem(pos);std::cout<<"client close sockfd!!!"<<std::endl;}else{close(_rfds[pos].fd);resetItem(pos);std::cerr<<"recv error"<<std::endl;}//buffer反序列化获得request//func处理request,获取response//response反序列化,获得返回信息std::string response= _func(buffer);write(_rfds[pos].fd,response.c_str(),response.size());}//hanlderevents处理读事件void handlerReadevents(){//监听套接字的读事件就绪if(_rfds[0].revents&POLLIN)accepter();for(int i=1;i<num;++i){if(_rfds[i].fd==defaultfd)continue;//连接套接字的读事件就绪if(!(_rfds[i].events&POLLIN))continue;if(_rfds[i].revents&POLLIN)recver(i);//else if(){}//else if(){}}}void start(){//只处理读事件int timeout=-1;while(true){int n=poll(_rfds,num,timeout);switch(n){case 0:cout<<"poll timeout"<<endl;log(NORMAL,"poll  timeout");break;case -1:log(ERROR,"poll error,errno=%d,strerror=%s",errno,strerror(errno));break;default://有读事件就绪//找到哪个套接字的读事件就绪了handlerReadevents();break;}sleep(1);}}~pollServer(){if(_listenSockfd>0)close(_listenSockfd);delete[] _rfds;}private:int _listenSockfd;uint16_t _port;struct pollfd* _rfds;func_t _func;};
}

sock.hpp

#pragma oncce 
#include <iostream>
#include <string>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <strings.h>
#include <unistd.h>
#include <sys/wait.h>
#include "Log.hpp"
#include "public.hpp"
using namespace std;class Sock{static const int backlog=5;
public://创建套接字文件描述符static int Socket(){int sockfd=socket(AF_INET,SOCK_STREAM,0);if(sockfd==-1){log(ERROR,"create a  socket error");exit(SOCKET_ERR);}else log(NORMAL,"create socket success, sockfd=%d",sockfd);//地址复用int opt=1;setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR|SO_REUSEPORT,&opt,sizeof(opt));return sockfd;}//给套接字绑定端口号static void Bind(int sockfd,uint16_t port){struct sockaddr_in local;socklen_t len=sizeof(local);bzero(&local,len);local.sin_family=AF_INET;local.sin_addr.s_addr=INADDR_ANY;local.sin_port=htons(port);int n=bind(sockfd,(const struct sockaddr*)&local,len);if(n==-1){log(ERROR,"bind sockfd error");exit(BIND_ERR);}else log(NORMAL,"bind sockfd success");}//监听套接字static void Listen(int sockfd){int n=listen(sockfd,backlog);if(n==-1){log(ERROR,"listen sockfd error");exit(BIND_ERR);}else log(NORMAL,"listen sockfd success");}//获取新连接套接字static int Accept(int listenSockfd,string* clientIp,uint16_t* clientPort){struct sockaddr_in client;socklen_t len=sizeof(client);bzero(&client,len);int sockfd=accept(listenSockfd,(struct sockaddr*)&client,&len);if(sockfd==-1)log(ERROR,"accept sockfd error");else {log(NORMAL,"accept a link socket=%d",sockfd);*clientIp=inet_ntoa(client.sin_addr);*clientPort=ntohs(client.sin_port);}return sockfd;}
};

Log.hpp

#pragma once
#include <iostream>
#include <time.h>
#include <stdarg.h>
#include <cstdio>
#define NORMAL 0
#define DEBUG 1
#define WARNING 2
#define ERROR 3
#define FATAL 4
using namespace std;
class LogMessage{
private:string log_path="./log.txt";string err_path="./err.txt";static string getLevel(int level){switch(level){case NORMAL:return "NORMAL";break;case DEBUG:return "DEBUG";break;case WARNING:return "WARNING";break;case ERROR:return "ERROR";break;case FATAL:return "FATAL";break;default:return "OTHER";}}string getTime(){time_t now=time(nullptr);struct tm* t=localtime(&now);int year=t->tm_year+1900;int mon=t->tm_mon+1;int day=t->tm_mday;int hour=t->tm_hour;int min=t->tm_min;int sec=t->tm_sec;char buffer[64];sprintf(buffer,"%d-%02d-%02d %02d:%02d:%02d",year,mon,day,hour,min,sec);return buffer;}
public:void operator()(int level,const char* format,...){string lev=getLevel(level);string time=getTime();va_list list;va_start(list,format);char msg[1024];vsnprintf(msg,sizeof(msg),format,list);FILE* log=fopen(log_path.c_str(),"a+");FILE* err=fopen(err_path.c_str(),"a+");if(log!=nullptr&&err!=nullptr){FILE* cur=nullptr;if(level==NORMAL||level==DEBUG){cur=log;}else cur=err;fprintf(cur,"[%s][%s][%s]\n",lev.c_str(),time.c_str(),msg);fclose(log);fclose(err);}                                                                                                                            }
};
static LogMessage log; 

public.hpp

#pragma once
#include <iostream>
enum{SOCKET_ERR=1,BIND_ERR,LISTEN_ERR,ACCEPT_ERR,USAGE_ERR};

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

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

相关文章

# filezilla连接 虚拟机ubuntu系统出错“尝试连接 ECONNREFUSED - 连接被服务器拒绝, 失败,无法连接服务器”解决方案

filezilla连接 虚拟机ubuntu系统出错“尝试连接 ECONNREFUSED - 连接被服务器拒绝&#xff0c; 失败&#xff0c;无法连接服务器”解决方案 一、问题描述&#xff1a; 当我们用filezilla客户端 连接 虚拟机ubuntu系统时&#xff0c;报错“尝试连接 ECONNREFUSED - 连接被服务…

TCP编程API

这里写自定义目录标题 主要的 TCP 编程 API 函数1.1 socket()1.2 bind()1.3 listen()1.4 accept()1.5 connect()1.6 send()1.7 recv()1.8 close() 主要的 TCP 编程 API 函数 1.1 socket() 创建一个新的套接字。 int socket(int domain, int type, int protocol);domain&…

STM32WB55RG开发(1)----开发板测试

STM32WB55RG开发----1.开发板测试 概述硬件准备视频教学样品申请源码下载产品特性参考程序生成STM32CUBEMX串口配置LED配置堆栈设置串口重定向主循环演示 概述 STM32WB55 & SENSOR是一款基于STM32WB55系列微控制器的评估套件。该套件采用先进的无线通信技术&#xff0c;支…

人工智能、机器学习与深度学习:层层递进的技术解读

引言 在当今科技快速发展的时代&#xff0c;人工智能&#xff08;AI&#xff09;已经成为一个热门话题&#xff0c;几乎渗透到了我们生活的方方面面。从智能手机的语音助手&#xff0c;到自动驾驶汽车&#xff0c;再到医疗诊断中的图像识别&#xff0c;人工智能的应用正在改变我…

【Java学习】电脑基础操作和编程环境配置

CMD 在Windows中用命令行的方式操作计算机。 打开CMD Win R输入CMD按下回车键 Win E 进入我的电脑 常用的CMD命令 盘符名称冒号 说明&#xff1a;盘符切换 举例&#xff1a;E:回车&#xff0c;表示切换到E盘 dir 说明&#xff1a;查看当前路径下的内容 cd目录 说明&a…

Mac中禁用系统更新

Mac中禁用系统更新 文章目录 Mac中禁用系统更新1. 修改hosts&#xff0c;屏蔽系统更新检测联网1. 去除系统偏好设置--系统更新已有的小红点标记 1. 修改hosts&#xff0c;屏蔽系统更新检测联网 打开终端&#xff0c;执行命令&#xff1a; sudo vim /etc/hosts127.0.0.1 swdis…

Unity3D UI 双击和长按

Unity3D 实现 UI 元素双击和长按功能。 UI 双击和长按 上一篇文章实现了拖拽接口&#xff0c;这篇文章来实现 UI 的双击和长按。 双击 创建脚本 UIDoubleClick.cs&#xff0c;创建一个 Image&#xff0c;并把脚本挂载到它身上。 在脚本中&#xff0c;继承 IPointerClickHa…

sql专题 之 where和join on

文章目录 前言where介绍使用过滤结果集关联两个表 连接外连接内连接自然连接 使用inner join和直接使用where关联两个表的区别总结 前言 从数据库查询数据时&#xff0c;一张表不足以查询到我们想要的数据&#xff0c;更多的时候我们需要联表查询。 联表查询我们一般会使用连接…

LeetCode 热题100之 动态规划1

对于动态规划的问题&#xff0c;解题步骤有以下几部(总结为动态规划五部曲&#xff1a;参考代码随想录动态规划 确定dp数组以及下标的含义&#xff1b;确定递推公式&#xff1b;dp数组如何初始化&#xff1b;确定遍历顺序&#xff1b;举例推导dp数组 下面的解题思路分析都将从…

python可视化将多张图整合到一起(画布)

这周有点事忙着&#xff0c;没时间重温刚结束的Mathurcup数学建模&#xff0c;这两天也是再看了下&#xff0c;论文还是赶紧挺烂的&#xff0c;但比国赛又有进步&#xff08;说起国赛又不得不抱怨了&#xff0c;基本其余省份都发了&#xff0c;但江西......哎&#xff09;。哎&…

MFC图形函数学习07——画扇形函数

绘制扇形函数是MFC中绘图的基本函数&#xff0c;它绘制的仍是由椭圆弧与椭圆中心连线构成的椭圆扇形&#xff0c;特例是由圆弧与圆心连线构成的圆扇形。 一、绘制扇形函数 原型&#xff1a;BOOL Pie(int x1,int y1,int x2,int y2,int x3,int y3,int x4,int y4); …

vue 依赖注入(Provide、Inject )和混入(mixins)

Prop 逐级透传问题​ 通常情况下&#xff0c;当我们需要从父组件向子组件传递数据时&#xff0c;会使用 props。想象一下这样的结构&#xff1a;有一些多层级嵌套的组件&#xff0c;形成了一棵巨大的组件树&#xff0c;而某个深层的子组件需要一个较远的祖先组件中的部分数据。…

手机上用什么方法可以切换ip

手机上用什么方法可以切换IP&#xff1f;在某些特定情境下&#xff0c;用户可能需要切换手机的IP地址&#xff0c;以满足网络安全、隐私保护或绕过地域限制等需求。下面以华为手机为例&#xff0c;将详细介绍手机IP地址切换的几种方法&#xff0c;帮助用户轻松实现这一目标。 一…

一个强大的Stable Diffusion comfyUI 工作流,能实现写真自由、各种风格融合、面部特征一致性等等

今天&#xff0c;我们将向您介绍一款非常实用的工具——Stable Diffusion comfyUI工作流。这款工作流基于Stable Diffusion技术&#xff0c;旨在为您提供一键式生成图像的便捷体验。无论您是AI绘画的新手还是专业人士&#xff0c;这个工作流都能为您带来极大的便利。 在这个教…

外泌体相关基因肝癌临床模型预测——2-3分纯生信文章复现——6.外泌体基因功能注释(二)

内容如下: 1.外泌体和肝癌TCGA数据下载 2.数据格式整理 3.差异表达基因筛选 4.预后相关外泌体基因确定 5.拷贝数变异及突变图谱 6.外泌体基因功能注释 7.LASSO回归筛选外泌体预后模型 8.预后模型验证 9.预后模型鲁棒性分析 10.独立预后因素分析及与临床的相关性分析…

【Homework】【1--4】Learning resources for DQ Robotics in MATLAB

Learning resources for DQ Robotics in MATLAB Lesson 1 代码 % Step 2: Define the real numbers a1 and a2 a1 123; a2 321;% Step 3: Calculate and display a3 a1 a2 a3 a1 a2; disp([a3 (a1 a2) , num2str(a3)])% Step 4: Calculate and display a3 a1 * a2 a3…

linux命令详解,存储管理相关

存储管理 一、内存使用量&#xff0c;free free 命令是一个用于显示系统中物理内存&#xff08;RAM&#xff09;和交换空间&#xff08;swap&#xff09;使用情况的工具 free -m free -m -s 5参数 -b 功能: 以字节&#xff08;bytes&#xff09;为单位显示内存使用情况。说…

推荐一款功能强大的视频修复软件:Apeaksoft Video Fixer

Apeaksoft Video Fixer是一款功能强大的视频修复软件&#xff0c;专门用于修复损坏、不可播放、卡顿、画面失真、黑屏等视频问题。只需提供一个准确且有效的样本视频作为参考&#xff0c;该软件就能将受损视频修复到与样本视频相同的质量。该软件目前支持MP4、MOV、3GP等格式的…

Redis如何保证数据不丢失(可靠性)

本文主要以学习为主&#xff0c;详细参考&#xff1a;微信公众平台 Redis 保证数据不丢失的主要手段有两个&#xff1a; 持久化 多机部署 我们分别来看它们两的具体实现细节。 1.Redis 持久化 持久化是指将数据从内存中存储到持久化存储介质中&#xff08;如硬盘&#xf…

第三十九章 基于VueCli自定义创建项目

目录 1. 选择创建模式 2. 选择需要的功能 3. 选择历史模式还是哈希模式 ​4.CSS预处理器 5. 选择ESLint规则 6. 开始创建项目 ​7. 自定义项目最终结构 1. 选择创建模式 输入创建的项目名&#xff0c;创建项目&#xff1a; 这里选择自定义模式&#xff1a; 2. 选择需要…