多路转接select服务器

目录

select函数原型

select服务器

select的缺点


前面介绍过多路转接就是能同时等待多个文件描述符,这篇文章介绍一下多路转接方案中的select的使用

select函数原型

#include <sys/select.h>
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set
*exceptfds, struct timeval *timeout);

先介绍一下各个参数以及返回值

多路转接需要等待多个文件描述符的事件就绪,所以用户势必需要告诉操作系统,他关心的是哪些文件描述符,以及关心这些文件描述符上的读事件还是写事件。读事件就绪就是这个文件描述符的缓冲区不为空有数据能读,写事件就绪就是缓冲区不为满可以写入。除了这两种常见的事件外,还可以关心某个文件描述符的异常事件。

再来看select的参数,nfds是一个整数,可以告诉操作系统需要关心哪些文件描述符,具体来说,nfds是需要关心的文件描述符的最大值 + 1,可以预想到select函数会遍历小于等于nfds - 1的文件描述符,查看是否有事件就绪

struct timeval {time_t      tv_sec;  /* Seconds */          //秒suseconds_t tv_usec; /* Microseconds */     //毫秒
};

timeout表示select的等待时间,timeout也可为空,表示阻塞等待直到某个文件描述符发生事件,timeout为0表示不等待事件发生,其他自定义值表示若在这段时间内没有事件发生,则超时返回。

返回值为0表示超时返回;为-1表示有错误发生,并设置错误码errno;为正数表示在timeout时间内事件就绪的文件描述符个数

为了介绍剩下的三个参数,先介绍一下fd_set

我们已经通过fds告诉操作系统要关心哪些文件描述符,timeout设置了等待时间,现在还需要告诉操作系统要关心哪些文件描述符的读事件或写事件

从抽象的层面上理解,fd_set是一个集合,是一个文件描述符的集合,readfds是关心读事件的文件描述符集合,writefds是关心写事件的文件描述符集合,exceptfds是关心异常事件的文件描述符集合。

还需要指出,这三个参数还是输出型参数,操作系统会将等待后事件就绪的文件描述符加入集合,

比如关心4,5,6的读事件,若就绪了4和5,集合就会变成4,5,这也为写代码带来了麻烦

从具体实现上来看,fd_set是一个位图,有若干个比特位表示文件描述符,值为1表示关心这个文件描述符,为0表示不关心,举个例子

00011111001

下标从0开始的话,这个位图表示关心3,4,5,6,7,10号文件描述符,其余的都不关心

/* fd_set for select and pselect.  */
typedef struct{/* XPG4.2 requires this member name.  Otherwise avoid the namefrom the global namespace.  */
#ifdef __USE_XOPEN__fd_mask fds_bits[__FD_SETSIZE / __NFDBITS];
# define __FDS_BITS(set) ((set)->fds_bits)
#else__fd_mask __fds_bits[__FD_SETSIZE / __NFDBITS];
# define __FDS_BITS(set) ((set)->__fds_bits)
#endif} fd_set;

fd_set封装了一个大小固定的数组,数组的每个比特位都可以记录是否关心这个文件描述符

作为用户,想对fd_set操作,操作系统也提供了相关的接口

// 将文件描述符fd从集合set中删除
void FD_CLR(int fd, fd_set *set);
// 判断文件描述符fd是否在集合set中
int  FD_ISSET(int fd, fd_set *set);
// 将fd放入集合set中
void FD_SET(int fd, fd_set *set);
// 清空集合set
void FD_ZERO(fd_set *set);

select服务器

到这里,select已经可以等待多个文件描述符的一些事件了,可以来搭一个简单的服务器,接收多个用户的消息,回显在屏幕上

这里只给出select_server的代码,其他文件的代码对理解select不重要,只需要了解套接字的使用便可轻松看懂,若要查看其他文件的代码,详见rokobo/wsl_code - Gitee.com

在这份代码中,需要等待的事件有等待客户端的连接和等待客户端发消息,由于连接建立后会创建文件描述符,文件描述符会变多,需要一个数据结构把这些文件描述符管理起来,这里选择了原生数组,因为可以直观的感受到select的缺点之一,存在大量遍历,性能不够高。

#include "socket.hpp"
#include "Log.hpp"
#include <sys/select.h>
#include <memory>
#include <cstring>
#include <cerrno>
using namespace SocketModule;
using namespace LogModule;class select_server
{
// sizeof可以得到底层数组的字节数,乘8得到比特数
static const int NUM = sizeof(fd_set) * 8;
public:select_server():_listen_sock(std::make_shared<TcpSocket>()),_is_running(false){}void init(int port){_listen_sock->BuildTcpSocketMethod(port);for(int i=0;i<NUM;++i){fds[i] = -1;}//初始只需要关心_listen_sock这一个文件描述符fds[0] = _listen_sock->Fd();}void loop(){_is_running = true;int listenfd = _listen_sock->Fd();fd_set readset;while(_is_running){//readset作为输出参数,select返回后可能被修改,需要清空后重新设置FD_ZERO(&readset);int max_fd = 0;for(int i=0;i<NUM;++i){if(fds[i] != -1){max_fd = fds[i] > max_fd ? fds[i] : max_fd;FD_SET(fds[i], &readset);}}struct timeval timeout = {2, 0};int ret = select(max_fd + 1, &readset, nullptr, nullptr, &timeout);if(ret == -1){LOG(LogLevel::ERROR) << "Error message: " << strerror(ret);continue;}else if(ret == 0){LOG(LogLevel::INFO) << "Time out\n";continue;}else{LOG(LogLevel::INFO) << "Dispatch begin\n";// 给不同种类的文件描述符分发不同的任务dispatcher(readset);}}  }void accepter(int fd){InetAddr client;auto client_sock = _listen_sock->Accepter(&client);if(client_sock == nullptr){LOG(LogLevel::ERROR) << "Accept error";return;}int client_fd = client_sock->Fd();if(client_fd < 0){LOG(LogLevel::ERROR) << "Client fd error";return;}//将client_fd加入到fds中//如果fds满了,关闭连接int i=0;for(i=0;i<NUM;++i){if(fds[i] == -1){fds[i] = client_fd;LOG(LogLevel::INFO) << "Accept success: " << client_sock->Fd() << " " << client.Addr();break;}}if(i == NUM){LOG(LogLevel::ERROR) << "Too many connections";client_sock->Close();return;}}void recver(int who){int fd = fds[who];std::string buffer;auto client_sock = std::make_shared<TcpSocket>(fd);ssize_t ret = client_sock->Recv(&buffer);if(ret == -1){LOG(LogLevel::ERROR) << "Recv error" << strerror(errno);client_sock->Close();//将fd从fds中删除fds[who] = -1;return;}else if(ret == 0){LOG(LogLevel::INFO) << "Client closed: " << client_sock->Fd();client_sock->Close();//将fd从fds中删除fds[who] = -1;return;}else{LOG(LogLevel::INFO) << "Recv success: " << buffer;return;}}void dispatcher(fd_set &readset){//找到所有合法的fd,分发for(int i=0;i<NUM;++i){if(fds[i] == -1)continue;if(FD_ISSET(fds[i], &readset)){//分发给处理连接的函数if(fds[i] == _listen_sock->Fd()){accepter(fds[i]);}//分发给处理IO的函数else{recver(i);}}}}void stop(){}
private:std::shared_ptr<TcpSocket> _listen_sock;int fds[NUM];bool _is_running;
};

主函数

#include "select_server.hpp"
#include <string>
int main()
{select_server s_svr;s_svr.init(8080);s_svr.loop();return 0;
}

 

select的缺点

从代码中大量的遍历,甚至select底层还要遍历,可以感受到select有太多遍历,效率不高,而且fd_set的底层数组是静态的无法扩容,能同时关心的文件描述符有限,而且需要用户自己去定义数据结构管理需要关心的文件描述符,更是增加了编码的复杂性,每次调用select,都需要把fd_set从用户态拷贝到内核态,这个拷贝的开销在fd很多时开销很大

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

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

相关文章

QT6 源(45):分隔条 QSplitter 允许程序的用户修改布局,程序员使用 IDE时,就是分隔条的用户,以及其 QSplitter 源代码

&#xff08;1&#xff09; &#xff08;2&#xff09;本类的继承关系如下&#xff0c;所以说分隔条属于容器&#xff1a; &#xff08;3&#xff09;本类的属性&#xff1a; &#xff08;4&#xff09; 这是一份 QSplitter 的举例代码&#xff0c;注意其构造函数时候的传参&am…

VSCode PIO使用Jlink SWD烧录Stm32

一、背景 PIO的编译速度比Arduino快很多&#xff0c;同样支持Arduino的语法。VScode的自动补全和插件也能够帮助快速开发目前使用JLINK SWD的方式连接STM32 二、配置 在ini配置文件中&#xff0c;添加如下内容 [env:genericSTM32F103C8] platform ststm32 board genericS…

JavaScript 渲染内容爬取:Puppeteer 入门

在现代网络应用中&#xff0c;许多网页内容是通过 JavaScript 渲染生成的&#xff0c;传统的爬虫工具往往难以获取这些动态内容。Puppeteer 作为一种强大的浏览器自动化工具&#xff0c;为这一问题提供了优雅的解决方案。本文将带你入门 Puppeteer&#xff0c;介绍如何安装、启…

卷积神经网络:视觉炼金术士的数学魔法

引言&#xff1a;当数学遇见视觉炼金术 在人工智能的奇幻世界里&#xff0c;卷积神经网络&#xff08;CNN&#xff09;犹如掌握视觉奥秘的炼金术士&#xff0c;将原始像素的"铅块"淬炼成认知的"黄金"。这种融合数学严谨性与生物灵感的算法架构&#xff0c…

Android Cordova 开发 - Cordova 快速入门(Cordova 环境配置、Cordova 第一个应用程序)

一、Cordova 1、Cordova 概述 Cordova 是使用 HTML&#xff0c;CSS 和 JavaScript 构建混合移动应用程序的平台 2、Cordova 特征 &#xff08;1&#xff09;命令行界面&#xff08;Cordova CLI&#xff09; 这是可用于启动项目&#xff0c;构建不同平台的进程&#xff0c;…

ubuntu18.04启动不了修复

参考: 虚拟机里的Ubuntu18.4启动时进入到grub rescue救援模式&#xff08;无法正常进入到系统&#xff09;&#xff0c;ls查看后只有一个硬盘和分区&#xff0c;且无法找到/boot/grub文件【已解决】_ubuntu grub rescue-CSDN博客 本人fdisk错误使用,导致了grub启动不了 第一步…

SpringBoot3设置maven package直接打包成二进制可执行文件

注意事项 SpringBoot普通native打包顺序clean compile spring-boot:process-aot native:compile 使用以下配置只会的打包顺序clean package&#xff08;注意&#xff1a;使用此配置以后打包会有编译后的class文件、jar包、original源文件、二进制可执行文件【Linux是无后缀的包…

【华为】防火墙双击热备-之-主备模式-单外网线路

FW1和FW2的业务接口都工作在三层&#xff0c;上行连接二层交换机。上行交换机连接运营商的接入点&#xff0c;运营商为企业分配的IP地址为100.100.100.2。现在希望FW1和FW2以主备备份方式工作。正常情况下&#xff0c;流量通过FW1转发&#xff1b;当FW1出现故障时&#xff0c;流…

MYSQL之表的操作

1. 创建表 语法: CREATE TABLE table_name ( field1 datatype, field2 datatype, field3 datatype ) character set 字符集 collate 校验规则 engine 存储引擎; field 表示列名, datatype 表示列的类型character set 字符集, 如果没有指定字符集, 则以所在数据库的字符集为…

RAG进阶:Chroma开源的AI原生向量数据库

一、Chroma 核心概念与优势 1. 什么是 Chroma&#xff1f; Chroma 是一款开源的向量数据库&#xff0c;专为高效存储和检索高维向量数据设计。其核心能力在于语义相似性搜索&#xff0c;支持文本、图像等嵌入向量的快速匹配&#xff0c;广泛应用于大模型上下文增强&#xff0…

店匠科技摘得 36 氪“2025 AI Partner 创新大奖”

全场景 AI 方案驱动跨境电商数智化跃迁 4 月 18 日,36 氪 2025 AI Partner 大会于上海盛大开幕。大会紧扣“Super App 来了”主题,全力探寻 AI 时代的全新变量,探索 AI 领域下一个超级应用的无限可能性。在此次大会上,跨境电商独立站 SaaS 平台店匠科技(Shoplazza)凭借“店匠跨…

SQL技术终极指南:从内核原理到超大规模应用

一、DDL核心应用场景与最佳实践 1.1 表结构设计场景矩阵 业务场景核心语法要素典型实现案例电商用户画像JSON字段虚拟列索引CREATE TABLE users (id INT, profile JSON, AS (profile->>$.age) VIRTUAL, INDEX idx_age((profile->>$.age)))物联网时序数据分区表压…

吴恩达深度学习作业CNN之ResNet实现(Pytorch)

课程中认识许多CNN架构。首先是经典网络&#xff1a; LeNet-5AlexNetVGG 之后是近年来的一些网络&#xff1a; ResNetInceptionMobileNet 经典网络 LeNet-5 LeNet-5是用于手写数字识别&#xff08;识别0~9的阿拉伯数字&#xff09;的网络。它的结构如下&#xff1a; 网络…

FPGA入门学习Day1——设计一个DDS信号发生器

目录 一、DDS简介 &#xff08;一&#xff09;基本原理 &#xff08;二&#xff09;主要优势 &#xff08;三&#xff09;与传统技术的对比 二、FPGA存储器 &#xff08;一&#xff09;ROM波形存储器 &#xff08;二&#xff09;RAM随机存取存储器 &#xff08;三&…

SqlSugar与Entity Framework (EF)的SWOT分析

以下是基于 SWOT 分析法 对 SqlSugar 和 Entity Framework (EF) 的特性对比&#xff1a; SqlSugar 优势 (Strengths) 高性能&#xff1a; SqlSugar 以轻量化设计著称&#xff0c;执行速度更快&#xff0c;适合对性能要求较高的场景。在大数据量操作和复杂查询中表现优异。 易…

学习记录:DAY16

Maven 进阶与前端实战 前言 二轮考核的内容下来了&#xff0c;由整体项目构建转为实现特定模块的功能。对细节的要求更高了&#xff0c;而且有手搓线程池、手搓依赖注入等进阶要求&#xff0c;又有得学力。嘻嘻&#xff0c;太简单了&#xff0c;只要我手搓 Spring Boot 框架……

深度学习--卷积神经网络调整学习率

文章目录 前言一、学习率1、什么学习率2、什么是调整学习率3、目的 二、调整方法1、有序调整1&#xff09;有序调整StepLR(等间隔调整学习率)2&#xff09;有序调整MultiStepLR(多间隔调整学习率)3&#xff09;有序调整ExponentialLR (指数衰减调整学习率)4&#xff09;有序调整…

【消息队列RocketMQ】四、RocketMQ 存储机制与性能优化

一、RocketMQ 存储机制详解 1.1 存储文件结构​ RocketMQ 的存储文件主要分布在store目录下&#xff0c;该目录是在broker.conf配置文件中通过storePathRootDir参数指定的&#xff0c;默认路径为${user.home}/store 。主要包含以下几种关键文件类型&#xff1a;​ 1.1.1 Comm…

C++入门小馆: 探寻vector类

嘿&#xff0c;各位技术潮人&#xff01;好久不见甚是想念。生活就像一场奇妙冒险&#xff0c;而编程就是那把超酷的万能钥匙。此刻&#xff0c;阳光洒在键盘上&#xff0c;灵感在指尖跳跃&#xff0c;让我们抛开一切束缚&#xff0c;给平淡日子加点料&#xff0c;注入满满的pa…

CSS-跟随图片变化的背景色

CSS-跟随图片变化的背景色 获取图片的主要颜色并用于背景渐变需要安装依赖 colorthief获取图片的主要颜色. 并丢给背景注意 getPalette并不是个异步方法 import styles from ./styles.less; import React, { useState } from react; import Colortheif from colorthief;cons…