26-LINUX--I/O复用-select

一.I/O复用概述

        /O复用使得多个程序能够同时监听多个文件描述符,对提高程序的性能有很大帮助。以下情况适用于I/O复用技术:

TCP 服务器同时要处理监听套接字和连接套接字。
服务器要同时处理 TCP 请求和 UDP 请求。
程序要同时处理多个套接字。
客户端程序要同时处理用户输入和网络连接。
服务器要同时监听多个端口
        需要指出的是,I/O 复用虽然能同时监听多个文件描述符,但它本身是阻塞的。并且当
多个文件描述符同时就绪时,如果不采取额外的措施,程序就只能按顺序依处理其中的每一
个文件描述符,这使得服务器看起来好像是串行工作的。如果要提高并发处理的能力,可以
配合使用多线程或多进程等编程方法

二.select机制

1.select接口介绍

        select 系统调用的用途是:在一段指定时间内,监听用户感兴趣的文件描述符的可读、
可写和异常等事件。
        select 系统调用的原型如下:
 #include <sys/select.h>int select(int maxfd, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct ti
meval *timeout);/*select 成功时返回就绪(可读、可写和异常)文件描述符的总数。如果在超时时间内
没有任何文件描述符就绪,select 将返回 0。select 失败是返回-1.如果在 select 等待
期间,程序接收到信号,则 select 立即返回-1,并设置 errno 为 EINTR。maxfd 参数指定的被监听的文件描述符的总数。它通常被设置为 select 监听的所
有文件描述符中的最大值+1readfds、writefds 和 exceptfds 参数分别指向可读、可写和异常等事件对应的文件
描述符集合。应用程序调用 select 函数时,通过这 3 个参数传入自己感兴趣的文件
描述符。select 返回时,内核将修改它们来通知应用程序哪些文件描述符已经就绪
fd_set 结构如下:#define __FD_SETSIZE 1024typedef long int __fd_mask;#define __NFDBITS (8 * (int) sizeof (__fd_mask))typedef struct{#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_ZERO(fd_set *fdset); // 清除 fdset 的所有位FD_SET(int fd, fd_set *fdset); // 设置 fdset 的位 fdFD_CLR(int fd, fd_set *fdset); // 清除 fdset 的位 fdint FD_ISSET(int fd, fd_set *fdset);// 测试 fdset 的位 fd 是否被设置timeout 参数用来设置 select 函数的超时时间。它是一个 timeval 结构类型的指针,采用指针参数是因为内核将修改它以告诉应用程序 select 等待了多久。timeval结构的定义如下:struct timeval{long tv_sec; //秒数long tv_usec; // 微秒数};//struct timeval tv = {5,0};如果给 timeout 的两个成员都是 0,则 select 将立即返回。如果 timeout 传递
NULL,则 select 将一直阻塞,直到某个文件描述符就绪*/

2.设计思路图解

3.测试代码

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/select.h>
#include<time.h>#define STDIN 0
int main()
{int fd = STDIN;//键盘fd_set fdset;//集合,收集描述符while(1)//因为不止检测一次{FD_ZERO(&fdset);//清空集合,每个位置0:FD_ZEROFD_SET(fd,&fdset);//将描述符fd添加到集合fdsetstruct timeval tv = {5,0};//超时时间int n = select(fd+1,&fdset,NULL,NULL,&tv);//可能阻塞if(n ==-1)//select执行失败{printf("select err\n");}else if(n==0)//超市,没有找到可用事件描述符{printf("tme out\n");}else{if(FD_ISSET(fd,&fdset)){char buff[128]={0};int num = read(fd,buff,127);printf("num=%d,buff=%s\n",num,buff);}}}
}
~                                                                                                                                                                                            
~                                                                                                                                                                                            
~                         

4.tcp通过select实现并发连接

SER.C

#include<stdio.h>      // 标准输入输出库
#include<stdlib.h>     // 标准库,提供一些函数如malloc, free, rand等
#include<string.h>    // 字符串操作库
#include<unistd.h>    // UNIX标准函数库
#include<sys/select.h>// 选择库,提供select函数
#include<time.h>      // 时间库
#include<sys/socket.h>// 套接字库
#include<arpa/inet.h> // 提供inet_addr等函数
#include<netinet/in.h>// 提供一些网络相关的宏#define MAXFD 10       // 定义最大文件描述符数量// 初始化socket函数
int socket_init();// 初始化文件描述符数组
void fds_init(int fds[]){for(int i=0; i<MAXFD; i++){fds[i] = -1; // 将所有文件描述符初始化为-1,表示未被使用}
}// 将新的文件描述符添加到数组中
void fds_add(int fds[], int fd){for(int i=0; i<MAXFD; i++){if(fds[i] == -1){ // 找到数组中第一个未使用的文件描述符位置fds[i] = fd;  // 添加文件描述符break;        // 退出循环}}
}// 从未使用的文件描述符数组中删除指定的文件描述符
void fds_del(int fds[], int fd){for(int i=0; i<MAXFD; i++){if(fds[i] == fd){ // 找到要删除的文件描述符fds[i] = -1;   // 将其设置为-1,表示未使用break;         // 退出循环}}
}// 接受客户端连接请求并添加到文件描述符数组
void accept_client(int sockfd, int fds[]){int c = accept(sockfd, NULL, NULL); // 接受连接if(c < 0){return; // 如果返回-1,表示出错}printf("accept c = %d\n", c);fds_add(fds, c); // 添加到文件描述符数组
}// 接收客户端发送的数据
void recv_date(int c, int fds[]){char buff[128] = {0}; // 创建缓冲区int n = recv(c, buff, 127, 0); // 接收数据if(n < 0){printf("cli close\n");close(c);          // 如果接收失败,关闭连接fds_del(fds, c);   // 从数组中删除该文件描述符return;}if(n == 0){printf("time out(%d)\n", n); // 如果超时}printf("buff(c=%d)=%s\n", c, buff); // 打印接收到的数据send(c, "ok", 2, 0);             // 发送确认消息
}// 主函数
int main(){int sockfd = socket_init(); // 初始化socketif(sockfd == -1){exit(1); // 如果初始化失败,退出程序}int fds[MAXFD]; // 文件描述符数组fds_init(fds);  // 初始化数组fds_add(fds, sockfd); // 将监听的socket添加到数组fd_set fdset; // 创建文件描述符集合while(1){ // 无限循环等待事件FD_ZERO(&fdset); // 清空文件描述符集合int maxfd = -1; // 存储最大的文件描述符// 遍历文件描述符数组,将所有文件描述符添加到集合中for(int i=0; i<MAXFD; i++){if(fds[i] == -1){continue; // 如果文件描述符未使用,跳过}FD_SET(fds[i], &fdset); // 添加到集合if(fds[i] > maxfd){ // 更新最大文件描述符maxfd = fds[i];}}struct timeval tv = {5,0}; // 设置超时时间// 使用select等待直到有文件描述符准备好IO操作或超时int n = select(maxfd+1, &fdset, NULL, NULL, &tv);if(n == -1){printf("select err\n"); // 错误} else if(n == 0){printf("time out\n"); // 超时} else{// 遍历文件描述符数组,检查哪些文件描述符准备好了IO操作for(int i=0; i<MAXFD; i++){if(fds[i] == -1){continue; // 如果文件描述符未使用,跳过}if(FD_ISSET(fds[i], &fdset)){ // 检查文件描述符是否被设置if(fds[i] == sockfd){ // 如果是监听的socketaccept_client(sockfd, fds); // 接受新的连接} else{ // 如果是已连接的客户端recv_date(fds[i], fds); // 接收数据}}}}}
}// 创建socket并绑定到端口
int socket_init(){int sockfd = socket(AF_INET, SOCK_STREAM, 0); // 创建socketif(sockfd == -1){return -1; // 创建失败返回-1}struct sockaddr_in saddr; // 服务器地址结构memset(&saddr, 0, sizeof(saddr)); // 清零saddr.sin_family = AF_INET; // 地址族saddr.sin_port = htons(6000); // 端口saddr.sin_addr.s_addr = inet_addr("127.0.0.1"); // IP地址int res = bind(sockfd, (struct sockaddr*)&saddr, sizeof(saddr)); // 绑定if(res == -1){printf("bind err\n");return -1; // 绑定失败返回-1}if(listen(sockfd, 5) == -1){ // 开始监听,设置队列长度为5return -1; // 监听失败返回-1}return sockfd; // 返回socket文件描述符
}

CLI.C

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>int main()
{int sockfd = socket(AF_INET,SOCK_STREAM,0);if(sockfd == -1){exit(1);}struct sockaddr_in saddr;//代表服务器的端口memset(&saddr,0,sizeof(saddr));saddr.sin_family = AF_INET;saddr.sin_port = htons(6000);saddr.sin_addr.s_addr = inet_addr("127.0.0.1");int res = connect(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));if(res == -1){printf("connct err\n");exit(1);}while(1){printf("input: ");char buff[128]={0};fgets(buff,128,stdin);if(strncmp(buff,"end",3)==0){break;}send(sockfd,buff,strlen(buff)-1,0);memset(buff,0,128);recv(sockfd,buff,127,0);printf("buff=%s\n",buff);}close(sockfd);exit(0);
}

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

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

相关文章

Python 连接 MySQL 及 SQL增删改查(主要使用sqlalchemy)

目录 一、环境 二、MySQL的连接和使用 2.1方式一&#xff1a;sql为主 2.1.1创建连接 2.1.2 表结构 2.1.3 新增数据 ​编辑 2.1.4 查看数据 ​编辑 2.1.5 修改数据 2.1.6 删除数据 2.2方式二&#xff1a;orm对象关系映射 2.2.1 mysql连接 2.2.2 创建表 2.2.3 新增…

关于 Redis 中集群

哨兵机制中总结到&#xff0c;它并不能解决存储容量不够的问题&#xff0c;但是集群能。 广义的集群&#xff1a;只要有多个机器&#xff0c;构成了分布式系统&#xff0c;都可以称之为一个“集群”&#xff0c;例如主从结构中的哨兵模式。 狭义的集群&#xff1a;redis 提供的…

Java里面的10个Lambda表达式必须掌握,提高生产力

目录 Java里面的10个Lambda表达式必须掌握&#xff0c;提高生产力 前言 1. 使用Lambda表达式进行集合遍历 2. 使用Lambda表达式进行集合过滤 3. 使用Lambda表达式进行集合映射 4. 使用Lambda表达式进行集合排序 5. 使用Lambda表达式进行集合归约 6. 使用Lambda表达式进…

使用docker-compose搭建达梦数据库主备集群

目录 1. Docker集群的搭建 2. 检查主备数据库 3. 主备集群的JDBC连接设置 1. Docker集群的搭建 达梦的镜像文件都是tar文件&#xff0c;通过docker load命令导入&#xff1a; docker load -i dm8_20240422_x86_rh6_64_rq_ent_8.1.3.140.tar 成功导入后&#xff0c;可看到…

刚刚❗️德勤2025校招暑期实习测评笔试SHL测评题库已发(答案)

&#x1f4e3;德勤 2024暑期实习测评已发&#xff0c;正在申请的小伙伴看过来哦&#x1f440; ㊙️本次暑期实习优先考虑2025年本科及以上学历的毕业生&#xff0c;此次只有“审计及鉴定”“税务与商务咨询”两个部门开放了岗位~ ⚠️测评注意事项&#xff1a; &#x1f44…

【JAVASE】java语法(成员变量与局部变量的区别、赋值运算符中的易错点)

一&#xff1a;成员变量与局部变量的区别 区别 成员变量 局部变量 类中位置不同 …

Java:110-SpringMVC的底层原理(上篇)

SpringMVC的底层原理 在前面我们学习了SpringMVC的使用&#xff08;67章博客开始&#xff09;&#xff0c;现在开始说明他的原理&#xff08;实际上更多的细节只存在67章博客中&#xff0c;这篇博客只是讲一点深度&#xff0c;重复的东西尽量少说明点&#xff09; MVC 体系结…

2024 AEE | 风丘科技将亮相日本爱知国际会展中心——共同创造!

2024年名古屋汽车工程博览会&#xff08;Automotive Engineering Exposition 2024 NAGOYA&#xff09;将于7月17-19日在日本爱知县国际展示场&#xff08;Aichi Sky Expo&#xff09;开展。本展会是专门为活跃在汽车行业的工程师和研究人员举办的汽车技术展览&#xff0c;汇聚了…

Web自动化测试-掌握selenium工具用法,使用WebDriver测试Chrome/FireFox网页(Java

目录 一、在Eclipse中构建Maven项目 1.全局配置Maven 2.配置JDK路径 3.创建Maven项目 4.引入selenium-java依赖 二、Chrome自动化脚本编写 1.创建一个ChromeTest类 2.测试ChromeDriver 3.下载chromedriver驱动 4.在脚本中通过System.setProperty方法指定chromedriver的…

操作系统期末复习整理知识点

操作系统的概念&#xff1a;①控制和管理整个计算机系统的硬件和软件资源&#xff0c;并合理地组织调度计算机的工作和资源的分配&#xff1b;②提供给用户和其他软件方便的接口和环境&#xff1b;③是计算机中最基本的系统软件 功能和目标&#xff1a; ①操作系统作为系统资源…

搭贝请假审批应用

在现代企业管理中&#xff0c;高效的请假审批系统至关重要。搭贝的请假审批应用通过简化员工的请假流程、提升管理层的工作效率&#xff0c;确保企业运作的连贯性和透明度。本文将介绍搭贝请假审批应用的主要功能模块&#xff1a;请假分析看板、请假申请审批流、请假类型维护和…

RocketMq源码解析六:消息存储

一、消息存储核心类 rocketmq消息存储的功能主要在store这个模块下。 核心类就是DefaultMessageStore。我们看下其属性 // 配置文件 private final MessageStoreConfig messageStoreConfig; // CommitLog 文件存储实现类 private final CommitLog commitLog; …

Mybatis05-一对多和多对一处理

多对一和一对多 多对一 多对一的理解&#xff1a; 多个学生对应一个老师 如果对于学生这边&#xff0c;就是一个多对一的现象&#xff0c;即从学生这边关联一个老师&#xff01; 结果映射&#xff08;resultMap&#xff09;&#xff1a; association 一个复杂类型的关联&…

PHP实现抖音小程序用户登录获取openid

目录 第一步、抖音小程序前端使用tt.login获取code 第二步、前端拿到code传给后端 第三步、方法1 后端获取用户信息 第四步、方法2 抖音小程序拿到用户信息把用户信息传给后端 code2Session抖音小程序用户登录后端文档 第一步、抖音小程序前端使用tt.login获取code 前端 …

leetcode 所有可能的路径(图的遍历:深度优先和广度优先)

leetcode 链接&#xff1a; 所有可能的路径 1 图的基本概念 1.1 有向图和无向图 左边是有向图&#xff0c;右边是无向图。对于无向图来说&#xff0c;图中的边没有方向&#xff0c;两个节点之间只可能存在一条边&#xff0c;比如 0 和 1 之间的边&#xff0c;因为是无向图&am…

线性代数|机器学习-P12Ax=b条件下x最小值问题

文章目录 1. Axb下的最值问题-图形转换2. Gram-Schmidt 标准形3. 迭代法-Krylov子空间法 1. Axb下的最值问题-图形转换 假设我们有一个直线方程如下&#xff1a; 3 x 1 4 x 2 1 \begin{equation} 3x_14x_21 \end{equation} 3x1​4x2​1​​ 在二维平面上&#xff0c;各个范…

DDMA信号处理以及数据处理的流程---原始数据生成

Hello&#xff0c;大家好&#xff0c;我是Xiaojie&#xff0c;好久不见&#xff0c;欢迎大家能够和Xiaojie一起学习毫米波雷达知识&#xff0c;Xiaojie准备连载一个系列的文章—DDMA信号处理以及数据处理的流程&#xff0c;本系列文章将从目标生成、信号仿真、测距、测速、cfar…

LLVM 后端执行流程

异构计算程序工作流程 图4-1中的LLVM后端的主要功能是代码生成&#xff0c;其中包括若干指令生成分析转换pass&#xff0c;将LLVM IR 转换为特定目标架构的机器代码 LLVM 流水线结构 输入指令经过图4-2中的各个阶段&#xff0c;从最初的LLVM IR&#xff0c;逐步演化为Selectio…

管理数据必备;侦听器watch用法详解,vue2与vue3中watch的变化与差异

目录 一、侦听器&#xff08;watch&#xff09;是什么&#xff1f; 二、Vue2中的watch&#xff08;Options API&#xff09; 2.1、函数式写法 2.2、对象式写法 ①对象式基础写法 ②回调函数handler ③deep属性 ④immediate属性 三、Vue3中的watch 3.1、向下兼容&#xff…

两句话让LLM逻辑推理瞬间崩溃!!

一道简单的逻辑问题&#xff0c;竟让几乎所有的LLM全军覆没&#xff1f; 对于人类来说&#xff0c;这个名为「爱丽丝梦游仙境」&#xff08;AIW&#xff09;的测试并不算很难—— 「爱丽丝有N个兄弟&#xff0c;她还有M个姐妹。爱丽丝的兄弟有多少个姐妹&#xff1f;」 稍加思考…