news 2026/6/2 19:21:58

介绍网络编程中的Select

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
介绍网络编程中的Select

我认为现在用select更多是作为没有epoll的情况下才用,因为epoll相比于select,能支持更高的并发量同时对系统的负担也小,不过select因为在任何系统下都可以用,所以在面对并发量不高且短连接同时不是必须用epoll的情况下也可以适当用用select。下面是select的介绍。

Select 是一种操作系统提供的机制,让一个线程能同时监听多个文件描述符(socket),只要其中任意一个 “就绪”(可读 / 可写 / 异常),就立刻通知程序处理。
Select一共包含6个内容:select,fd_set结构体,FD_ZERO,FD_SET,FD_ISSET,FD_CLR
一.select
int select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, struct timeval *timeout);
1.为这些fd中最大的一个,因为fd是int值的数,012分别对应标准输入,标准输出和标准错误。这三个是系统已经分配好的,所以fd的连接从3开始依次增加,但是因为 select 内部是从0开始遍历到nfds - 1的,为了包含最大的那个 fd,必须 +1。比如目前fd最大是5,如果不加1,则内部就会遍历0~4,导致把5漏掉。
2.是可读的fd集合。作用是监听哪些事件可读。只要有数据可读,便会返回这个fd的值。比如监听的是3456,可读的是35,那么便会返回35这两个fd。
3.是可写的fd集合。作用是监听哪些事件可写。当socket发送缓冲区有空位或者可以调用 send或write发数据,就会返回对应的fd。如监听的是3456,可写的是45,那么就返回45这两个fd。
4.是异常的fd集合。作用是监听哪些fd发生了异常错误。当发生错误或进入异常状态时,便会返回对应的fd。如监听的是3456,6发生了错误了,就会返回6这个fd。
5.是超时时间。作用是最多等多久。这个结构体内部有两个参数,tv_sec(秒)和tv_usec(微秒)。这里大致分为三种:NULL,0,指定时间。
NULL是直到有事件来,否则一直堵塞。0是立刻返回。也可以设置5秒(tv_sec = 5)。

二.fd_set结构体
fd_set本质是比特位集合,你可以想象成是一个长度固定的bit数组,每一位代表一个fd是否被监听。它的大小默认是1024。

三.FD_ZERO(fd_set *set);
作用是把fd_set里所有的位全部清空,变成 0,起到重置的作用。因为在创建一个fd_set变量时,其内部有可能是一堆随机的1,如果不清零会导致后续出现问题。

四.FD_SET(int fd, fd_set *set);
作用是把指定的fd加入到fd_set集合里,并监听这个fd。其内部的操作是把fd在fd_set集合中对应的那一位置1。

五.FD_ISSET(int fd, fd_set *set);
作用是判断指定的fd是否已经“就绪”,就绪指select函数中的可读,可写或异常。返回0是未就绪,返回非0是已经就绪。

六.FD_CLR(int fd, fd_set *set);
作用是将对应的fd从fd_set集合中删除。其内部的操作是把fd在fd_set集合中对应的那一位置0。

七.代码流程示范

intmain(intargc,charconst*argv[]){//创建socketintsockfd=socket(AF_INET,SOCK_STREAM,0);structsockaddr_inserveraddr;serveraddr.sin_family=AF_INET;serveraddr.sin_addr.s_addr=htons(INADDR_ANY);serveraddr.sin_port=htons(2000);//绑定if(-1==bind(sockfd,(structsockaddr*)&serveraddr,sizeof(structsockaddr_in))){printf("bind error\n");return-1;}//监听listen(sockfd,10);//select初始化structsockaddr_inclientaddr;socklen_tlen=sizeof(clientaddr);fd_set rfds,rset;//清空FD_ZERO(&rfds);//把sockfd加到rfdsFD_SET(sockfd,&rfds);intmaxfd=sockfd;//循环关注while(1){//把总集合给rset,因为本代码只关注可读事件rset=rfds;//只关注哪些可读,不关注的填NULLintnready=select(maxfd+1,&rset,NULL,NULL,NULL);//判断是否可读if(FD_ISSET(sockfd,&rset)){intclientfd=accept(sockfd,(structsockaddr*)&clientaddr,&len);printf("accept finished: %d\n",clientfd);//把客户端连接加到rfds里FD_SET(clientfd,&rfds);//如果新的客户端fd大于之前的maxfd,就更新//客户端断开连接后会被系统回收,如果还未完成回收就有新的连接,那么会为其在分配maxfd+1;//如果已经完成回收,新连接的fd就会用之前回收的值if(clientfd>maxfd)maxfd=clientfd;}//判断可读集合中的每一个是否就绪inti=0;for(i=sockfd+1;i<=maxfd;i++){if(FD_ISSET(i,&rset)){charbuffer[1024]={0};intcount=recv(i,buffer,1024,0);//count = 0代表客户端正常关闭连接if(count<=0){printf("client disconnect: %d\n",i);//关闭fdclose(i);FD_CLR(i,&rfds);continue;}printf("RECV: %s\n",buffer);count=send(i,buffer,count,0);printf("SEND: %d\n",count);}}}

零声社区资源链接:https:github.com/0voice

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/2 19:17:14

Arduino交通信号灯项目:从电路原理到代码实现的嵌入式开发入门

1. 项目概述与核心价值几年前&#xff0c;当我第一次尝试用Arduino点亮一个LED时&#xff0c;那种“代码驱动物理世界”的奇妙感觉至今难忘。从那个闪烁的小灯开始&#xff0c;我逐渐深入到各种嵌入式项目中&#xff0c;而交通信号灯模型&#xff0c;几乎是我向每一个想入门硬件…

作者头像 李华
网站建设 2026/6/2 19:16:06

信号频谱图纵坐标为啥是负的?一个公式帮你从物理意义理解清楚

信号频谱图纵坐标为何是负值&#xff1f;从物理本质到工程实践的深度解析 第一次打开频谱分析仪或运行MATLAB的 pwelch 函数时&#xff0c;许多工程师都会被一个现象困惑&#xff1a;为什么那些代表信号强度的纵坐标值竟然是负数&#xff1f;难道是仪器出了问题&#xff0c;还…

作者头像 李华
网站建设 2026/6/2 19:14:43

保姆级教程:在Ubuntu 20.04上从源码编译运行ORB-SLAM3(含ROS/非ROS版本)

从零构建ORB-SLAM3&#xff1a;Ubuntu 20.04全流程实战指南当你第一次接触视觉SLAM系统时&#xff0c;最令人兴奋的莫过于看到自己编译的程序成功运行在真实数据集上。ORB-SLAM3作为当前最先进的视觉惯性SLAM框架之一&#xff0c;其多传感器支持能力和稳定的表现使其成为学习与…

作者头像 李华
网站建设 2026/6/2 19:06:03

超越供应商SDK:为RK3568构建高性能Qt+QML开发环境的完整心路与配置

深度解锁RK3568的QtQML开发潜能&#xff1a;从硬件加速到团队协作的全链路实践当RK3568遇上Qt框架&#xff0c;这颗国产芯片的Mali-G52 GPU本应成为嵌入式GUI开发的利器。但现实情况是&#xff0c;许多开发团队发现官方提供的Qt SDK在QML渲染性能上表现糟糕——动画卡顿、界面响…

作者头像 李华