当前位置: 首页 > news >正文

【计算机网络】IO复用方法(二)——Select

目录

​ 

一、select 方法概述

二、select 系统调用原型

三、select 的基本原理

四、使用 select 的步骤

五、将select应用到tcp服务端

六、select 的优缺点

、select 方法概述

select 是 Unix/Linux 系统中一种经典的 I/O 多路复用技术,允许程序监视多个文件描述符(如套接字、管道等),直到其中一个或多个描述符准备好进行 I/O 操作(如可读、可写或发生异常)。select 适用于需要同时处理多个 I/O 通道的场景,如网络服务器、客户端等。

二、select 系统调用原型

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

参数设置:

1、nfds参数:指定被监听的文件描述符的总数。它通常被设置为select 监听的所有文件描述符中的最大值加1,因为文件描述符是从0开始计数的。

2、readfds、writefds和exceptfds参数分别指向可读、可写和异常等事件对应的文件描述符集合。应用程序调用select函数时,通过这3个参数传入自己感兴趣的文件描述符。select调用返回时,内核将修改它们来通知应用程序哪些文件描述符已经就绪。一般只关注读,后面两个参数设置为空指针。这三个参数是fd_set结构指针类型。

3、timeout:代表超时时间;设为 NULL 表示无限阻塞,设为 0 表示非阻塞轮询。

三、select 的基本原理

select 通过三个文件描述符集合(fd_set)来监视不同类型的 I/O 事件:

  • readfds:监视描述符是否可读(如数据到达、连接关闭)。
  • writefds:监视描述符是否可写(如发送缓冲区空闲)。
  • exceptfds:监视描述符是否发生异常(如带外数据到达)。

调用 select 时,内核会阻塞进程,直到至少一个描述符就绪或超时。返回后,内核会修改这些集合,仅保留就绪的描述符。

给select传入一个集合,会返回告诉这个集合中有多少个就绪,集合中就绪对应的位会被设置成1,没有就绪就会设置成0。最多可以检测1024给描述符。同时select为周期性的调用。

四、使用 select 的步骤

int main(){int fd=STDIN;//输入fd_set fdset;while (1)//把描述符添加到集合中{FD_ZERO(&fdset);//清空集合FD_SET(fd,&fdset);//将描述符添加到集合里struct timeval tv={5,0};//设置超时时间,时间每次重置int n=select(fd+1,&fdset,NULL,NULL,&tv);//阻塞,最多阻塞5sif(n==-1){printf("select err\n");}else if(n==0){//超时printf("time out\n");}else{if(FD_ISSET(fd,&fdset))//真,fd有读事件发生{char buff[128]={0};read(fd,buff,127);printf("read:%s\n",buff);}}
}

初始化描述符集合: 使用 FD_ZERO 清空集合,FD_SET 添加需要监视的描述符。

调用 select 传入初始化后的集合和超时时间。select 会阻塞直到事件就绪或超时。

检查返回值

  • 返回 -1 表示错误(如被信号中断)。
  • 返回 0 表示超时。
  • 返回正数表示就绪的描述符数量。

处理就绪的描述符 使用 FD_ISSET 检查哪些描述符就绪,并执行相应操作

五、将select应用到tcp服务端

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/select.h>
#include <time.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>#define MAXFD 10int socket_init();
void fds_init(int fds[]){//初始化数组for(int i = 0; i < MAXFD; i++){fds[i] = -1;}
}
//添加
void fds_add(int fds[], int fd){for(int i = 0; i < MAXFD; i++ ){if( fds[i] == -1){fds[i] = fd;break;//当找到一个-1,就停止}}
}
//移除
void fds_del(int fds[], int fd){for(int i = 0; i < MAXFD; i++){if( fds[i] == fd){fds[i] = -1;break;}}
}
int main(){int sockfd = socket_init();//套接字if( sockfd == -1){exit(1);}int fds[MAXFD];//数组fds_init(fds);//-1,初始化数组,为空fds_add(fds,sockfd);//添加描述符fd_set fdset;//selectwhile( 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( maxfd < fds[i]){//找到最大的描述符maxfd = fds[i];}}struct timeval tv = {5,0};//超时时间int n = select(maxfd+1,&fdset,NULL,NULL,&tv);//阻塞,5sif( n == -1){printf("select err\n");}else if( n == 0 ){//超时printf("time out\n");}else{//有n个描述符就绪,遍历整个数组找到有效的for(int i = 0; i < MAXFD; i++){if( fds[i] == -1){//无效描述符continue;}if( FD_ISSET(fds[i],&fdset)){//真,有事件发生,检测有无事件发生//判断套接字类型if( fds[i] == sockfd ){//监听套接字 acceptstruct sockaddr_in caddr;int len = sizeof(caddr);int c = accept(sockfd,(struct sockaddr*)&caddr,&len);if( c < 0 ){continue;}printf("accept c=%d\n",c);//接受链接fds_add(fds,c);  //把c加入到数组中,/*因为等待该循环将事件监测完毕,进入下一轮循环时,又重新清空集合,将数组的所有元素重新添加至集合,此时c也被加入到集合中,再次进行select检测,下一轮就有两个描述符以供检测*/}else{//连接套接字有数据 recvchar buff[128] = {0};int num = recv(fds[i],buff,127,0);if( num <= 0 ){close(fds[i]);fds_del(fds,fds[i]);printf("close\n");}else{printf("recv:%s\n",buff);send(fds[i],"ok",2,0);}}}}}}
}
int socket_init(){//创建监听套接字int sockfd = socket(AF_INET,SOCK_STREAM,0);if( sockfd == -1){//创建套接字失败return -1;}struct sockaddr_in saddr;//ipv4专用,制定ip端口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 = bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));//绑定,通用套接字if( res == -1){printf("bind err\n");return -1;}res = listen(sockfd,5);//监听队列if( res == -1){return -1;}return sockfd;
}

客户端:

服务端:

六、select 的优缺点

优点

  • 跨平台支持,几乎在所有 Unix-like 系统上可用。

  • 实现简单,适合少量连接或对性能要求不高的场景。

缺点

  • 文件描述符数量受限(通常为 1024),高并发场景性能较差。

  • 每次调用需重新传入描述符集合,内核和用户空间频繁拷贝数据。

  • 线性扫描所有描述符,效率随描述符数量增加而下降。

http://www.dtcms.com/a/552816.html

相关文章:

  • 【Java EE进阶 --- SpringBoot】统一功能处理(拦截器)
  • 主流数据分析工具全景对比:Excel / Python / R / Power BI / Tableau / Qlik / Snowflake
  • 从被动防御到主动管控:雷池SafeLine的远程安全运营之道
  • 人体静电消除器安全设计 蒙冬智能
  • 我想要个网站深圳最新招聘
  • Hybrid OCR-LLM框架用于在大量复杂密集企业级文档信息提取
  • 仙居做网站在哪里做项目网格化管理方案
  • ubuntu部署whisper+speaker_large+qwen【一】
  • 四大主流平台深度测评:2025企业自动化运维平台选型指南,自动化巡检平台适配关键场景
  • 计算机毕业设计 基于Python的热门游戏推荐系统的设计与实现 Django 大数据毕业设计 Hadoop毕业设计选题【附源码+文档报告+安装调试】
  • 每周读书与学习->JMeter主要元件详细介绍(三)逻辑控制器
  • QML学习笔记(四十八)QML与C++交互:QML中可实例化C++对象
  • 深信服上网行为 SANGFOR_AC_v11.0_AD域密码认证配置
  • RKNN-Toolkit2入门
  • 服务器公网IP、私网IP、弹性IP是什么?区别与应
  • 无锡哪家做网站好怎么做公司网站文案
  • php做网站架构图建站时候源码有验证怎么办
  • 10. 引用计数
  • 利用DeepSeek辅助改写luadbi-duckdb支持日期和时间戳数据类型
  • 用 Redis 的 List 存储库存队列,并通过 LPOP 原子性出队来保证并发安全案例
  • 定制开发开源AI智能名片S2B2C商城系统:新零售革命下云零售模式的创新实践
  • WebForms Validation
  • AI智能办公系统/企业OA办公/DeepSeek办公应用★pc/公众号/H5/App/小程序
  • 破局冷轧困境:RFID 赋能钢厂高效安全升级
  • 线程同步机制及三大不安全案例
  • Leetcode438. 找到字符串中所有字母异位词
  • 站内推广和站外推广的区别wordpress采集生成用户插件
  • 高清的网站制作iis7.0搭建网站
  • 使用 Docker Compose 部署 Spring Boot 应用:SmartAdmin 实战指南
  • 徐州建设企业网站网站建设与网站制作