多路复用-select

select 实现与应用

  • select 的原理
  • 基本函数
    • select
    • 对fd_set 操作
  • 用select搭建一个简单的服务端
  • 总结

select 的原理

 在网络IO一篇中我们讲到了5种的IO网络模型。而select则是多路复用中的一种。它把等待数据就绪和读取数据区分开,实现了单线程操作多个网络IO的功能。
 select如何实现单线程中对多个网络IO读取操作的呢?

内核根据io的3种状态,将各个状态准备好的io放入对应的位图中去。而后,我们根据对应的位图,去遍历io获取或写入数据。由此实现同时操作多个io。

重点小黑板:

  1. io的3种状态 可读、可写、是否出错
  2. 位图 fd_set,最大为1024位
  3. socket创建出来的fd是一个int型,而且是逐次递增的。如果中间关闭了一个fd,则下次创建的时候,新建的fd为之前关闭的fd值。

基本函数

fd_set: 该类型可以简单的理解为按 bit 位标记句柄的队列,例如标记一个值为8的句柄,则该fd_set的第8位标记为1.
头文件#include <arpa/inet.h>

select

  • 原型
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,struct timeval *timeout)
  • 参数介绍
参数名说明
nfds遍历到的最大socketfd
readfds获取到的可读的io列表
writefds获取到的可写的io列表
exceptfds获取到的出错的io列表
timeout读取io状态的间隔时间

timeout为NULL时,表示置于阻塞状态,一定等到监视文件描述符集合中某个文件描述符发生变化为止;
为0时,表示不管文件描述符是否有变化,都立刻返回继续执行,文件无变化返回0,有变化返回一个正值;
大于0时,该值为等待的超时时间,即select在timeout时间内阻塞,超时时间之内有事件到来就返回,否则在超时后一定返回,文件无变化返回0,有变化返回一个正值。

  • 函数功能
     用来获取出可读、可写、出错的io列表

  • 注意事项
    当新客户端connect的时候也会触发select的可读事件,故也需要处理

对fd_set 操作

函数名函数原型函数作用
FD_ISSETFD_ISSET(int fd, fd_set* fds)判断fd是否存在于fds
FD_SETFD_SET(int fd, fd_set* fds)在fds中标记fd的句柄
FD_CLRFD_CLR(int fd, fd_set* fds)将fd的标记从fds集合中清除
FD_ZEROFD_ZERO(fd_set* fds)清空fds里面所有的标记

用select搭建一个简单的服务端

#include <stdio.h>
#include <string.h>
#include <arpa/inet.h>
#include <unistd.h>#include <errno.h>#define BUFFER_LEN	1024int main()
{int sockfd = socket(AF_INET, SOCK_STREAM, 0);if(sockfd < 0){printf("create socket error!\n");return -1;}struct sockaddr_in addr;memset(&addr, 0, sizeof(struct sockaddr_in));addr.sin_family = AF_INET;addr.sin_port = htons(4018);addr.sin_addr.s_addr = INADDR_ANY;if(bind(sockfd, (struct sockaddr*)&addr, sizeof(struct sockaddr_in)) < 0){printf("bind socket error! \n");return -1;}if (listen(sockfd, 5) < 0){printf("listen socket error! \n");return -1;}fd_set rReadset,rfds;FD_ZERO(&rfds);FD_SET(sockfd, &rfds);int maxfd = sockfd;while (1){rReadset = rfds;// 这里只获取可读状态的ioint nready = select(maxfd + 1, &rReadset, NULL, NULL, NULL);if (nready < 0){printf("select error!\n");continue;}if (FD_ISSET(sockfd, &rReadset)){struct sockaddr_in clientAddr;memset(&clientAddr, 0, sizeof(struct sockaddr_in));int cliLen = sizeof(struct sockaddr_in);int clientfd = accept(sockfd, (struct sockaddr*)&clientAddr, (socklen_t *)&cliLen);if (clientfd < 0){printf("accept client error\n");continue;}char str[INET_ADDRSTRLEN] = {0};printf("recv from %s at port %d, sockfd:%d, clientfd:%d\n", inet_ntop(AF_INET, &clientAddr.sin_addr, str, sizeof(str)),ntohs(clientAddr.sin_port), sockfd, clientfd);if (maxfd == FD_SETSIZE){printf("clientfd out of range\n");break;}FD_SET(clientfd, &rfds);maxfd = clientfd > maxfd ? clientfd : maxfd;printf("sockfd:%d, max_fd:%d, clientfd:%d\n", sockfd, maxfd, clientfd);if (--nready == 0){continue;}}for (int i = sockfd + 1; i < maxfd + 1; i++){if (!FD_ISSET(i, &rReadset)){continue;}char recvBuf[BUFFER_LEN] = {0};int ret = read(i, recvBuf, BUFFER_LEN);if (ret < 0){if (errno == EAGAIN || errno == EWOULDBLOCK){printf("read all data early\n");}FD_CLR(i, &rfds);close(i);}else if (ret == 0){printf("connet is close %d\n", i);FD_CLR(i, &rfds);close(i);}else{printf("Recv: %s, %d Bytes\n", recvBuf, ret);}if (-- nready == 0){break;}}}return 0;
}

总结

fd_set 数据结构中只有1024位,故通常说法是select最高支持1024个连接。
select在select调用的时候也是阻塞的,因为kernel 会“监视”所有 select 负责的 socket,当任何一个 socket 中的数据准备好了,select 就会返回。

优点:只用单线程执行,占用资源少,不消耗太大的cpu资源,能够同时为多个客户端提供服务
缺点:当句柄值太大的时候,本身需要消耗大量的时间去轮询。很多操作系统提供了更为高效的接口,如linux提供了epoll,BSD提供了kqueue,Solaris提供了/dev/poll

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

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

相关文章

c++中各种运算符

c中各种运算符 1. 算术运算符&#xff1a; 加法运算符&#xff1a;减法运算符&#xff1a;-乘法运算符&#xff1a;*除法运算符&#xff1a;/取模运算符&#xff08;取余数&#xff09;&#xff1a;%&#xff08;取模运算符&#xff09; 2. 关系运算符&#xff1a; 相等运算…

探索设计模式的魅力:融合AI大模型与函数式编程、开启智能编程新纪元

​&#x1f308; 个人主页&#xff1a;danci_ &#x1f525; 系列专栏&#xff1a;《设计模式》 &#x1f4aa;&#x1f3fb; 制定明确可量化的目标&#xff0c;坚持默默的做事。 &#x1f680; 转载自热榜文章&#xff1a;探索设计模式的魅力&#xff1a;融合AI大模型与函数式…

葡萄书--图卷积网络

现有图神经网络皆基于邻居聚合的框架&#xff0c;即为每个目标节点通过聚合其邻居刻画结构信息&#xff0c;进而学习目标节点的表示 谱域方法&#xff1a;利用图上卷积定理从谱域定义图卷积。空间域方法&#xff1a;从节点域出发&#xff0c;通过在节点层面定义聚合函数来聚合…

如何把PDF转成word文件?三个好用的方法推荐给你

PDF转Word的问题&#xff0c;一直是许多人在处理文档时经常遇到的挑战。PDF作为一种广泛使用的文件格式&#xff0c;具有许多优点&#xff0c;如保持文档的原始布局、防止内容被轻易修改等。然而&#xff0c;这也意味着PDF文件在某些情况下不易编辑&#xff0c;特别是当需要对其…

Linux系统一键安装DataEase结合内网穿透实现公网访问本地WebUI界面

文章目录 前言1. 安装DataEase2. 本地访问测试3. 安装 cpolar内网穿透软件4. 配置DataEase公网访问地址5. 公网远程访问Data Ease6. 固定Data Ease公网地址 前言 DataEase 是开源的数据可视化分析工具&#xff0c;帮助用户快速分析数据并洞察业务趋势&#xff0c;从而实现业务…

蓝桥杯刷题-约数的个数

3377. 约数的个数 - AcWing题库 #include <bits/stdc.h>using namespace std;int n; int Get(int x) {int ans 0;for(int i 1;i < x / i; i ){if(x % i 0 && i ! x / i) ans 2;if(x % i 0 && i x / i) ans 1;}return ans; }int main() {cin &…

【WEEK8】学习目标及总结【MySQL+Spring Boot】【中文版】

学习目标&#xff1a; 完成MySQL部分的学习 开始学习SpringBoot 学习内容&#xff1a; 参考视频教程【狂神说Java】MySQL最新教程通俗易懂事务数据库连接池 参考视频教程【狂神说Java】SpringBoot最新教程IDEA版通俗易懂Spring Boot总览Spring Boot运行原理 学习时间及产出&a…

基于51单片机点滴输液控制系统LCD显示( proteus仿真+程序+设计报告+讲解视频)

基于51单片机点滴输液控制系统LCD显示 1. 主要功能&#xff1a;2. 讲解视频&#xff1a;3. 仿真设计4. 程序代码5. 设计报告6. 设计资料内容清单&&下载链接 基于51单片机点滴输液控制系统LCD显示( proteus仿真程序设计报告讲解视频&#xff09; 仿真图proteus7.8及以上…

SpringBoot 集成Swagger3

说明&#xff1a; 1&#xff09;、本文使用Spring2 集成Swagger3&#xff0c; 本想使用Springboot3 jdk 17 集成Swagger3, 但是搜了一些资料&#xff0c;Spring 想引用swagger3 需要依赖降级使用Spring2 版本&#xff0c;不然会出现下图的报错信息&#xff0c; 或者使用Sprin…

python笔记 | 哥德巴赫猜想

哥德巴赫猜想&#xff1a;每个不小于6的偶数都可以表示成两个素数之和。 素数&#xff1a;只能被1和自身整除的正整数。就是大于1且除了1和它本身之外没有其他因数的数。例如&#xff0c;2、3、5、7、11等都是素数&#xff0c;而4、6、8、9等则不是素数。 下面这段Python代码…

Python学习(四)文件操作

文件操作 想想我们平常对文件的基本操作&#xff0c;大概可以分为三个步骤(简称文件操作三步走): ① 打开文件 ② 读写文件 ③ 关闭文件 注意:可以只打开和关闭文件&#xff0c;不进行任何读写 在Python&#xff0c;使用open函数&#xff0c;可以打开一个已经存在的文件&…

【Java】处理工具类详解

在Java开发中&#xff0c;日期和时间的处理是一个绕不开的话题。Java自身提供了丰富的API来处理日期和时间&#xff0c;但是使用起来可能较为繁琐。因此&#xff0c;许多开发者会使用一些工具类来简化日期和时间的处理。本文将介绍一个常用的日期处理工具类——DateUtils&#…

LeetCode 2007.从双倍数组中还原原数组:哈希表——从nlogn到n

【LetMeFly】2007.从双倍数组中还原原数组&#xff1a;哈希表——从nlogn到n 力扣题目链接&#xff1a;https://leetcode.cn/problems/find-original-array-from-doubled-array/ 一个整数数组 original 可以转变成一个 双倍 数组 changed &#xff0c;转变方式为将 original …

环形链表的约瑟夫问题(牛客网)

/*** 代码中的类名、方法名、参数名已经指定&#xff0c;请勿修改&#xff0c;直接返回方法规定的值即可** * param n int整型 * param m int整型 * return int整型*/struct ListNode * BuyNode(int n)//创建节点和成环{ struct ListNode *pheadNULL;struct ListNode *ptailN…

吐血整理!跨境电商全年选品方向!一年12个月热点解析

一月 演出服、礼盒、贺卡、装饰品、彩带、拉花、红地毯、邀请函、荧光棒、泡泡机等。 二月 超级碗&#xff1a;投影仪、蓝牙音箱、超级碗电子游戏、望远镜、运动类产品等&#xff1b; 情人节&#xff1a;珠宝、服饰饰品、巧克力、香水、口红、烘焙用品、礼盒、个人护理、成…

MySQL常用命令和函数的讲解以及表之间的联结

Mysql的中一些语句的用法&#xff1a; 有表&#xff1a; CREATE TABLE book (id int(20) NOT NULL,book_name varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT 书名,press varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NUL…

pymavlink 解析自定义mavlink消息。

1.下载mavlink_master包&#xff0c;用于将xml 文件生成对应的py文件。地址是 https://codeload.github.com/mavlink/mavlink/zip/refs/heads/master 进入目录运行python .\mavgenerate.py 呈现gui程序 2.根据发送端的消息定义格式修改接收解析段的pymavlink有关库。 可修改……

基于大数据的手机销售数据分析可视化系统,爬取京东和淘宝的的手机商品数据进行分析,Flask,Python,数据可视化

介绍 该系统主要是通过爬取京东和淘宝的的手机商品数据进行分析。爬虫python脚本通过打开浏览器授权登录后按照搜索“手机”关键字后出现的商品列表进行爬取&#xff0c;获取标题名&#xff0c;解析付款人数&#xff0c;品牌&#xff0c;评论人数&#xff0c;发货地&#xff0…

自用-常用词

PHP 常用 file_put_contents("awlog.txt", ---time:.date(Y-m-d H:i:s,time()).---xml:.$GLOBALS[HTTP_RAW_POST_DATA].var_export($_POST,TRUE).PHP_EOL, FILE_APPEND); error_reporting(0); register_shutdown_function(function(){ var_dump(error_get_last()…

算法学习——LeetCode力扣补充篇14(179. 最大数、43. 字符串相乘、32. 最长有效括号、543. 二叉树的直径、113. 路径总和 II)

算法学习——LeetCode力扣补充篇14 179. 最大数 179. 最大数 - 力扣&#xff08;LeetCode&#xff09; 描述 给定一组非负整数 nums&#xff0c;重新排列每个数的顺序&#xff08;每个数不可拆分&#xff09;使之组成一个最大的整数。 注意&#xff1a;输出结果可能非常大&…