c++高级篇(二) ——Linux下IO多路复用之select模型

news/2024/10/18 23:27:08/

什么是IO多路复用

前言

我们在Linux上服务端一般是要同时连接多个客户端进行通信,但是为每一个客户端连接创建一个进/线程,会消耗很多资源,一个1核2GB的虚拟机,大概只能创建100多个线程,但是我们经常使用网络知道,这样是远远不能满足我们日常的使用需求的,所以为了解决这一问题,就需要我们去使用IO多路复用。

IO多路复用

IO多路复用指的是我们可以使用一个进/线程去处理多个TCP链接,减少系统开销,而我们常见的IO多路复用主要用三种:

  • select(1024)
  • poll(几千)
  • epoll(百万)

网络通讯中的读与写事件

读事件

  • 已连接队列中有已经准备好的socket(有新的客户端连接上来)
  • 接收缓存有数据可以读(对端发送的报文已经送达)
  • tcp连接断开(对端使用close()函数断开了连接)

写事件

  • 发送端缓冲区没有满,可以写入数据(向对端发送报文)

select模型

位图

  • 什么是位图

    select实现IO多路复用是基于位图来实现的,位图的本质是一个32位整型数组(int[32]),一个32位整型有4个字节,每个字节有8个位:
    32 ∗ 8 ∗ 4 = 1024 32*8*4=1024 3284=1024
    每一个位可以监听一个socket这也是select模型课件监听1024个socket的原因所在。

  • 位图的相关操作

    在Linux内核中为我们提供相关的宏让我们操作位图:

    void D_CLR(int fd,fd_set* set);//将socket从位图中删除
    int FD_ISSET(int fd,fd_set *set);//判断socket是否在位图中
    void FD_SET(int fd,fd_set* set);//将socket加入到位图中
    void FD_ZERO(fd_set* set); //将位图全部初始化为0
    

select模型的细节

写事件

  • 如果tcp的发送缓冲区没有满,那么此时socket连接是可写的
  • 一般来说发送缓冲区不容易填满,但是如果发送数据量过大或者网络带宽不够,发送缓冲区有填满的可能。

水平触发

  • select()监视的socket如果发生了事件,select()会返回(通知应用程序处理事件),如果事件没有被处理,再次调用select()的时候会立即再通知
  • 存在的问题
    • 这里操作位图的方法是轮询,它的性能会随着socket的增多而增多
    • 每次调用select,需要拷贝位图,而且select属于用户态,网络通信属于内核态,需要拷贝两次,会影响select的性能
    • 受位图大小的限制,每个进/线程selectt所能处理的socket数量默认是1024个,性能不够高,无法处理网络通信频繁的实际场景

select模型监控socket通讯流程图

在这里插入图片描述

代码示例

#include "data-sharing-center/public/_cmpublic.h"
#include <string.h>using namespace std;int inintserver(int port); //初始化监听端口int main(int argc,char* argv[])
{if(argc!=2){cout<<"using example:./server [port]"<<endl;return -1;}//初始化服务端用来监听的socketint listensock=inintsocket(atoi(argv[1]));if(listensock<0){perror("inintsocket() error");return -1;}cout<<"listensock="<<listensock<<endl;//初始化selectfd_set readfds;FD_ZERO(&readfds);FD_SET(listensock,&readfds);int maxfd=listensock;//记录当前监听socket的数量while(true)  //使用select循环监听{//定义超时结构体struct timeval timeout;  //定义超时结构体timeout.tv_sec=10; //秒timeout.tv_usec=0; //微妙fd_set tmps=readfds;  //select操作中会对位图进行修改,创建一个临时位图int infds=select(maxfd+1,&tmps,NULL,NULL,&timeout);  //开启监听if(infds<0)   //连接失败{perror("select() error");break;}else if(infds==0)  //超时(此时间段没有事件发生){cout<<"select timeout"<<endl;continue;}else    //有事件发生{//遍历位图,查看是哪一个socket发生事件for(int eventfd=0;eventfd<=maxfd;eventfd++){if(FD_ISSET(eventfd,&tmps))    //查看是否是该socket{if(eventfd=listensock)   //如果是监听,说明发生的事件是有客户顿socket发送了连接请求{//接收客户端连接struct sockaddr_in clientaddr;socklen_t addrlen=sizeof(clientaddr);int clientsock=accept(listensock,(struct sockaddr*)&clientaddr,&addrlen);if(clientsock<0){perror("accept() error");continue;}cout<<"client connected,clientsock:"<<clientsock<<endl;//将新的客户端socket加入位图FD_SET(clientsock,&readfds);if(clientsock>maxfd){maxfd=clientsock;}}else   //否则就是客户端向服务端发送了数据,或者有客户端断开了连接{char buffer[1024]; //用来接收数据memset(buffer,0,sizeof(buffer));if(recv(eventfd,buffer,sizeof(buffer),0)<0)   //说明是有客户端断开了{cout<<"client disconnected,clientfd="<<eventfd<<endl;close(eventfd);FD_CLR(eventfd,&tmps);if(maxfd==eventfd);  //重新计算maxfd的值,注意,只有当eventfd==maxfd时才需要计算。{for(int ii=maxfd;ii>0;ii--){if(FD_ISSET(ii,&readfds)){maxfd=ii;break;}}}}else   //说明是有客户端发送了数据{cout<<"client data:"<<buffer<<endl;send(eventfd,buffer,strlen(buffer),0);  //把数据发送回去说明已经收到了}}   }}}}return 0;
}int inintsocket(int port)
{int sock=socket(AF_INET,SOCK_STREAM,0);if(sock<0){perror("socket() error");return -1;}//设置端口复用int opt=1;unsigned int len=sizeof(opt);setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&opt,len);//绑定端口struct sockaddr_in serveraddr;serveraddr.sin_family=AF_INET;serveraddr.sin_port=htons(port);serveraddr.sin_addr.s_addr=htonl(INADDR_ANY);if(bind(sock,(struct sockaddr*)&serveraddr,sizeof(serveraddr))<0){perror("bind() error");close(sock);return -1;}//监听if(listen(sock,5)<0){perror("listen() error");close(sock);return -1;}return sock;}

注意: 这里的头文件是博主自己封装的,大家可以使用’man+函数名的方式查看相关函数所需的头文件。


http://www.ppmy.cn/news/1437728.html

相关文章

全量知识系统 程序详细设计 “智能”分类及标注的主题词架构(QA百度文库)

今天聊聊全量知识系统 程序详细设计中对“智能”的解读。先概列如下: 机器智能MI&#xff08;计算智能-脑智&#xff09;<“部件part”>人工智能AI&#xff08;感知智能-心智&#xff09;<”组件group”>机体智能OI (认知智能-元智) <“零件order”> 每一…

人脸红润锐化算法解决方案,满足企业多样化的视觉需求

高质量的视觉呈现已成为企业提升品牌形象、吸引用户注意力的关键所在&#xff0c;美摄科技作为业内领先的视觉技术提供商&#xff0c;近日推出了全新的人脸红润&锐化算法解决方案&#xff0c;致力于为企业带来前所未有的视觉体验。 该解决方案的核心在于其精准而灵活的人脸…

FSMC读取FPGA的FIFO

一、硬件说明 FSMC配置 单片机的代码如下&#xff1a; #define VALUE_ADDRESS_AD1 (__IO uint16_t *)0x60400000while (1){if(!HAL_GPIO_ReadPin(GPIOF, GPIO_PIN_8)) //数据非空{data *(__IO uint16_t *)VALUE_ADDRESS_AD1;data2 *(__IO uint16_t *)VALUE_ADDRESS_AD1…

学习 Rust 的第九天:如何使用结构体

好的&#xff0c;我已经收到完整的内容了。我会按照规则对其进行翻译&#xff0c;稍等片刻。 大家好&#xff0c; 今天是学习 Rust 的第九天&#xff0c;我们要讨论一个非常重要的概念&#xff0c;即 结构体&#xff08;structs&#xff09;&#xff0c;它可以将相关的数据组…

第5章 全局大喇叭——详解广播机制

第5章 全局大喇叭——详解广播机制 如果你了解网络通信原理应该会知道&#xff0c;在一个IP网络范围中&#xff0c;最大的IP地址是被保留作为广播地址来使用的。 比如某个网络的IP范围是192.168.0.XXX&#xff0c;子网掩码是255.255.255.0&#xff0c;那么这个网络的广播地址…

浅谈架构方法之时间片轮询

PS&#xff1a;最近在逛CSDN的时候偶然发现了一篇文章讲到了这个架构&#xff0c;发现之前做过一个项目就用了这个东西&#xff0c;于是我搜了一下&#xff0c;感觉挺多文章都不好理解&#xff0c;由于我也是最近才接触到这个东西&#xff0c;所以我决定自己也写一篇&#xff0…

HTML--标签

1.加粗标签 <strong>文字内容</strong> 2.分割线 <hr> 3.问题 4.HTML基本骨架 HTML基本骨架是网页模板。 html&#xff1a;整个网页 head&#xff1a;网页头部&#xff0c;存放浏览器看的代码&#xff0c;例如CSS。 body&#xff1a;网页主体&#xf…

设置Ollama在局域网中访问的方法(Ubuntu)

趁着Llama3的热度试了一下Ollama&#xff0c;果然部署推理大模型很有用。一个现实的需求是&#xff0c;如果我们要在局域网中访问Ollama上大模型的服务&#xff0c;应该怎么办呢&#xff1f;参考了一下其他博客的方法 例如&#xff1a;一分钱不花&#xff01;手把手教你部署Go…