高级IO_多路转接之select

文章目录

    • @[TOC](文章目录)
  • 前言
    • 文件描述符的阻塞和非阻塞
      • fcntl
  • 多路转接之select
    • select接口
    • select的缺点

前言

关于IO,我们已经用过了不少IO接口,从最简单的printf、scanf->C语言文件接口fprintf、fscanf->系统文件接口read、write->再到我们的系统网络套接字接口send、recv。

这些接口见证了我们的学习历程,但是这些接口真的就只有读写吗? 不光有读写,还有

就像是我们使用scanf的时候,需要我们去输入一些字符,程序才能继续运行,否则就会一直阻塞住,这就是等的过程。

实际等的过程,其实也可以理解为,等待资源准备就绪的过程。而当等的时候,该线程是会被挂起的,这是不是一种低效的表现呢?

等待是必然的,因为毕竟没有资源就绪,必须要等,直到资源就绪。但是我们可以将等待交给别人去做,我们可以继续做我们自己的事情。

这里的别人就是我们今天所需要学习的select,具体是如何实现的我们今天就来学习。


在学习select之前,我们先来了解一下文件描述符的阻塞和非阻塞。

文件描述符的阻塞和非阻塞

之前我们学习recv的时候,最后一个参数我们讲过是设置该函数为非阻塞还是阻塞的。
实际上,我们的文件描述符是可以对它设置阻塞和非阻塞的。

fcntl

fcntl函数是Linux系统中用于操作文件描述符的一个系统调用,它允许程序对文件描述符进行各种控制操作。
在这里插入图片描述
这里参数中的fd就是要对哪个文件描述符进行控制操作,参数cmd用来表明要对fd做怎样的控制操作。
这个函数的返回值根据不同的cmd操作有不同的意义。
在这里插入图片描述

我们今天要学习的cmd参数有 F_GETFL和F_SETFL。

如果这里cmd设置为F_GETFL,用来获取fd的文件描述符状态,如果获取成功,返回值会返回它的描述符状态,这个返回值是一个int,其实我们根据以往的经验就知道这是一个位图。

如果设置cmd设置为F_SETFL,用来设置fd的文件描述符状态,这时我们就可以传第三个参数将其设置为非阻塞。

示例代码

#include <iostream>
#include <cstdio>
#include <unistd.h>
#include <fcntl.h>
#include <cstring>
void SetUnblock(int fd)
{// 获取fd的属性int fl = fcntl(fd, F_GETFL);if (fl < 0){// 获取失败perror("F_GETFD Error");return;}int n = fcntl(fd, F_SETFL, fl | O_NONBLOCK);if (n < 0){perror("Set Nonblock Error");}else{std::cout << "Fd:" << fd << " ,set nonblock done" << std::endl;}
}
int main()
{// 将标准输入设置为非阻塞会发生什么?SetUnblock(0);while (1){char buffer[1024];// ssize_t n = scanf("%s",buffer);ssize_t n = read(0, buffer, sizeof(buffer) - 1);if (n > 0){buffer[n - 1] = 0;std::cout << buffer << std::endl;}if (n < 0){std::cout << "???" << std::endl;std::cout << "errno: " << errno << " ,str: "<<strerror(errno) << std::endl; sleep(1);}}return 0;
}

运行后可以发现我们的标准输入被设置成了非阻塞,如果我们键盘不输入数据,write就会一直返回-1,可是返回-1不是代表错误吗? 我们来看看错误码的描述。
在这里插入图片描述
错误码为11,描述为Resource temporarily unavailable(资源暂时不可用)。
这其实就是因为我们设置非阻塞的原因,所以我们要判断是read发生错误还是非阻塞了,我们就需要对errno进行EWOULDBLOCK判断。
在这里插入图片描述
在这里插入图片描述

多路转接之select

select作为多路转接的接口之一, 其作用就是将需要关心的文件描述符交给它来管理,可以让他一次性等待多个文件描述符,我们的线程只需要等select就可以了。如果有文件描述符资源就绪,就会立马返回。试想我们之前写过的服务器,我们将每一个连接都用一个线程来管理,可是我们一个进程可以创建的线程是有限的,而如果对于用户访问量大的服务来讲,这种一个线程维护一个连接的方式是非常低效的,因为每一个客户端不可能时时刻刻都在发送数据,这种情况必然会带来许多进程长时间处于挂起状态导致系统资源浪费。

select接口

在这里插入图片描述
参数int nfds,是要所需要关心的所有fd中最大的fd + 1。
参数fd_set *readfds,fd_set其实就是一个位图,我们可以用FD_SET,FD_ZERO,FD_ISSET来对fd_set进行设置,这个readfds就是我们要关心读事件的文件描述符是哪些,它是输入输出型函数。
参数fd_set *writefds,与readfds差不多,只不过它关心的是写事件,它是输入输出型函数。
参数fd_set *exceptfds,与readfds差不多,只不过它关心的是错误事件,它是输入输出型函数。
参数struct timeval *timeout,该结构体有两个成员变量代表秒和毫秒,用来设置对select的等待时间,当超过这个等待时间没有文件描述符就绪了,就会返回0,它是输入输出型函数。

示例代码

#include "Socket.hpp"
#include <sys/select.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/time.h>
#include <cstring>#define MAX_FDS 64
#define INVALID_FD -1const std::string default_ip = "0.0.0.0";
const uint16_t default_port = 8080;class SelectServer
{
public:SelectServer(uint16_t port = default_port): _port(port) {}inline void InitFds(){_fds[0] = _listensock._sockfd;for (int i = 1; i < MAX_FDS; ++i){// 第一次for循环// 初始化_fds_fds[i] = -1;}}void Init(){_listensock.Init();_listensock.Bind(AF_INET, default_ip, _port);_listensock.Listen();InitFds();}void UpdateSet(int *max_fd, fd_set *set){std::cout << "现有fds: ";for (int i = 0; i < MAX_FDS; ++i){// 第二次for循环// 更新readfdsif (_fds[i] == INVALID_FD){continue;}std::cout << _fds[i] << " ";FD_SET(_fds[i], set);if (*max_fd < _fds[i]){*max_fd = _fds[i];}}std::cout << std::endl;}void Accepter(){struct sockaddr_in tmp;socklen_t len = sizeof tmp;int newfd = accept(_listensock._sockfd, (struct sockaddr *)&tmp, &len);for (int i = 0; i < MAX_FDS; ++i){//第四次for循环if (_fds[i] == INVALID_FD){_fds[i] = newfd;lg(Info, "Get A New Sockfd:%d", newfd);break;}if (i == MAX_FDS){lg(Warning, "Fds Is Full, Newfd:%d", newfd);close(newfd);return;}}}void Handler(int fd, int i){char buffer[1024];memset(buffer, 0, sizeof buffer);int n = read(fd, buffer, sizeof buffer);if(n > 0){buffer[n] = 0;std::string mes = buffer;std::cout << mes;}else if (n < 0){lg(Warning, "Read Error...");_fds[i] = INVALID_FD;close(_fds[i]);}else{lg(Info, "Foreign Host Closed...");_fds[i] = INVALID_FD;close(_fds[i]);}}void Dispatch(const fd_set *readfds, const fd_set *writefds, const fd_set *exceptfdss){for (int i = 0; i < MAX_FDS; ++i){// 第三次循环,看_fds里哪些准备好了if(_fds[i] == INVALID_FD){continue;}else if (FD_ISSET(_fds[i], readfds)){if (_fds[i] == _listensock._sockfd){// acceptAccepter();continue;}Handler(_fds[i], i);}}}void Start(){fd_set readfds;struct timeval tv = {5, 0};while (1){int max_fd = -1;FD_ZERO(&readfds);UpdateSet(&max_fd, &readfds);tv = {5, 0};// tv = {0, 0};int n = select(max_fd + 1, &readfds, nullptr, nullptr, &tv); // 如果tv传nullptr将成为阻塞等待,传{0,0}会变成非阻塞等待if (n == 0){lg(Info, "Select Time Out...");continue;}else if (n < 0){lg(Warning,"Select Error...");std::cout << "errno:" << errno << " strerror:" << strerror(errno) << std::endl;}else{Dispatch(&readfds, nullptr, nullptr);}}}~SelectServer(){_listensock.Close();}private:int _fds[MAX_FDS];Socket _listensock;uint16_t _port;
};

在这里插入图片描述
通过select可以让一个线程同时管理多个连接!!

select的缺点

缺点1:由于其参数很多都是输入输出型函数,需要不断重置结构体。
缺点2:每次进行一次select都是一次从用户态拷贝数据到内核态的过程。
缺点3:一旦有文件描述符资源准备就绪,还是要遍历一次fds,仍有资源开销。
缺点4:它所能管理的fd有限,这个大小取决于fd_set的位数,可以通过查看FD_SETSIZE()来看能管理最大的fd。

所以后面我们还需要学习多路转接更好的接口poll和epoll。

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

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

相关文章

docker -run hello-world超时

主要原因就是尝试拉取库的时候没有从阿里云镜像里拉&#xff0c;所以设置一下就好了 这里使用的是ubuntu系统&#xff08;命令行下逐行敲就行了&#xff09; sudo mkdir -p /etc/docker sudo tee /etc/docker/daemon.json <<-EOF {"registry-mirrors": [&quo…

Unity Shader 极坐标

Unity Shader 极坐标 前言项目简单极坐标极坐标变体之方形极坐标变体之圆形拉花封装方法 鸣谢 前言 极坐标记录 项目 简单极坐标 极坐标变体之方形 极坐标变体之圆形 拉花 封装方法 鸣谢 【菲兹杂货铺】【Unity Shader教程】极坐标实现以及极坐标的两种变体

使用dot来画流程图

Dot是一种图形描述语言&#xff0c;属于Graphviz软件的一部分。Graphviz是一个用于可视化图形&#xff08;图表、网络图等&#xff09;的开源工具集。使用Dot语言&#xff0c;你可以创建并描述节点和边&#xff0c;从而生成图形。以下是如何使用Dot语言画图的基本步骤&#xff…

巴西市场有哪些电商平台?巴西最畅销的产品有哪些?

巴西&#xff0c;作为南美洲最大的经济体之一&#xff0c;近年来在电子商务领域展现出强劲的增长势头。随着互联网的普及和消费者购物习惯的改变&#xff0c;电商平台在巴西市场上“打”得热火朝天&#xff0c;不过占据市场份额最大的依然还是美客多。本文将探讨巴西市场上的主…

[C++][设计模式][访问器]详细讲解

目录 1.动机2.模式定义3.要点总结4.代码感受1.代码一2.代码二 1.动机 在软件构件过程中&#xff0c;由于需求的变化&#xff0c;某些类层次结构中常常需要增加新的行为(方法)&#xff0c;如果直接在基类中做这样的更改&#xff0c; 将会给子类带来很繁重的变更负担&#xff0c…

数据泄露时代的安全之道:访问认证的重要性

引言 想象一下&#xff1a;你一觉醒来&#xff0c;收到一条通知——你的公司遭遇了数据泄露。你感到恐惧&#xff0c;因为这意味着客户数据被曝光&#xff0c;公司声誉受损&#xff0c;还有巨额罚款在等着你。在当今的数字化环境中&#xff0c;这种情况太常见了。全球各地的组…

数据库组成及原理

属性&#xff1a; 把数据库中的一个表类比成一个公司&#xff0c;那么公司里的每个人都是一个“属性”&#xff08;表中的一个字段视为一个属性&#xff09;&#xff0c;不管老板还是员工&#xff0c;只要是公司里的人&#xff0c;就都是一个属性。 主键&#xff1a; 老板就是“…

R语言学习,入门

我是一名6年开发经验的程序员&#xff0c;后端&#xff0c;大数据&#xff0c;前端都会。 现在加入了医疗行业&#xff0c;要做数据分析&#xff0c;前同事的实验室生信专业的&#xff0c;用的是R语言&#xff0c;为了跑通他的程序。就来学一下吧&#xff0c;看了一下好像挺简…

色彩魔术-空气远近法

1. 定义 空气远近法&#xff0c;是使用色彩差异表现远近的一种方法&#xff0c;即色彩透视法。 色彩透视法&#xff1a;也称色彩远近法&#xff0c;它是空气透视法的一种&#xff0c;它也是跟空气的厚薄有关&#xff0c;景物距离观察者越远其颜色饱和度越低&#xff0c;亮度也…

尝试修改苍穹外卖为”李小罗餐厅“

学习苍穹外卖后&#xff0c;将其修改为自己所需要的项目&#xff0c;也是对苍穹外卖项目的加深理解 对项目之间的连接等关系进一步清晰&#xff0c;那么便开始吧 d1_开始修改 修改名字为”李小罗餐厅“ src\views\login\index.vue src\router.ts 结果展示 修改进来之后的展示…

nginx转发的问题

我在项目配置的时候遇到一个问题&#xff1a; 配置了域名转发&#xff0c;且配置了https nginx配置如下&#xff1a; server {listen 443 ssl;server_name yourdomain.com;ssl_certificate /path/to/your/certificate.crt;ssl_certificate_key /path/to/your/private.key;loca…

动物检测yolo格式数据集(水牛 、大象 、犀牛 、斑马四类)

动物检测数据集 1、下载地址&#xff1a; https://download.csdn.net/download/qq_15060477/89512588?spm1001.2101.3001.9500 2、数据集介绍 本数据集含有四种动物可以检测&#xff0c;分别是水牛 、大象 、犀牛 、斑马四类&#xff0c;数据集格式为yolo格式&#xff0c;…

【Java环境配置过程详解(包括IDEA配置Java)】

目录 一、JDK下载安装 1. 官网下载JDK 2. 本地安装JDK 3. 配置环境变量 4. 验证是否安装成功 ​编辑二、IDEA进行安装下载 1. 官网下载 IDEA 2、IDEA进行Java开发 1. 创建Java项目 2. 程序测试 一、JDK下载安装 1. 官网下载JDK 1&#xff09;官网链接: https://www.o…

Redis理解【精细】【快速上手】

目录 1. 了解3V和3高 2.什么是redis 3. redis可以做什么 4. Windows安装 5. 使用redis客户端操作redis 5.1 redis基本命令 5.1.1 切换数据库 5.1.2 查看当前数据库的大小 5.1.3 查看当前数据库所有的key ​​​​​​​ 5.1.4 清空当前数据库所有key 5.1.5 清空所…

网口串口(Serialport)服务器

文章所用工具http://t.csdnimg.cn/2gIR8http://t.csdnimg.cn/2gIR8 搭建服务器界面 操作配置文件保存方式类 public string FileName { get; set; }public IniHelper(string name) {this.FileName name; //在构造函数中给路径赋值} 1 先导入c语言进行读取操作ini文件的方法 …

Qt creator实现一个简单计算器

目录 1 界面设计 2 思路简介 3 代码 目录 1 界面设计 ​2 思路简介 3 代码 3.1 widget.h 3.2 widget.c 4 完整代码 在这里主要记载了如何使用Qt creator完成一个计算器的功能。该计算器可以实现正常的加减乘除以及括号操作&#xff0c;能实现简单的计算器功能。 1 界…

Qt Creator配置以及使用Git

Qt Creator配置以及使用Git 引言一、Qt Creator配置git二、Qt Creator使用git2.1 创建git仓库 or git项目导入Qt2.2 配置远端&#xff0c;拉代码 or 上传代码2.3 查看更改2.4 更多细节可参考官方文档 三、参考的博客以及文档 引言 Qt Creator配置Git之后&#xff0c;可以看作是…

使用Charles实现Android抓包,附带Charles破解教程

1.下载Charles 网址&#xff1a;下载Charles 安装完成后的界面&#xff1a; 2.配置http抓包 点击该选项 可以看到代理的 ip 和端口号 然后在手机的wifi中配置代理&#xff08;手机和电脑要在同一局域网&#xff09;&#xff0c;代理选择手动&#xff0c;并填入ip和端…

vue3中 slot使用

默认插槽&#xff1a; 这是最基本的插槽类型&#xff0c;当没有指定 name 属性时&#xff0c;插槽是默认插槽。 子组件&#xff1a; <template><div class"child"><h2>子组件内容</h2><slot></slot> <!-- 默认插槽&#x…

UnityUGUI之三 Text

富文本 常用语法&#xff1a; 1.加粗 <b> text </b> 2.斜体 <i> text </i> 3.尺寸 <size?> text </size> 4.颜色 <color#ff0000> text </color>