Linux服务器3 --- 多路IO复用技术 --- select(单进程服务端模型使用select实现一对多处理)讲解

多路IO复用技术可以监听多个网络IO上的网络事件,网络IO,一个socket就相当于一个网络IO,网络事件,网络IO上发生的读事件,写事件,异常事件等。
单进程服务器模型两个主要模块发生冲突导致我们只能实现一对一的处理,如果单进程模型加上IO复用技术,就可以实现一对多处理效果。
1、accept 函数是阻塞函数,为什么阻塞?第一个参数是serverfd,阻塞在serverfd这个socket上等事件,listen监听serverfd这个网络IO上的事件,比如读事件或者写事件,什么时候会触发读/写事件,别人给你发数据就会触发读事件,用serverfd这个socket去写数据就会触发写事件。等待连接就是listen在阻塞监听socket上的读事件,客户端发送握手请求,发给serverfd,导致serverfd读事件触发,被listen监听到,listen会去唤醒accept,accept与客户端进行连接,实质上是listen阻塞,监听是否有网络读事件发生,有就去唤醒accept,没有就继续监听。
2、recv函数与accept函数有类似的道理,阻塞在clientfd,clientfd网络IO上面也有读写事件,recv监听clientfd,客户端发送数据包,唤醒recv。accept和recv都在阻塞等待网络IO上发生读事件。
3、找一个人,既能监听serverfd上的读事件又能监听clientfd上的读事件,accept和recv不用监听,就不再阻塞等待读事件。select就是这个人,我们的帮手,同时监听若干个网络IO上的网络事件。以前accept是监听等待连接,recv是监听读取数据,现在将二者的监听都交给select,accept和recv不再监听,就不再阻塞。
4、具体流程
1)select有一个监听集合,select 监听最大数1024(也就是1个服务端,最多1023个客户端)每一位对应一个socket。先将serverfd放到监听集合中,将serverfd对应位置成1,表示开启监听,然后设置监听什么事件,比如现在设置监听serverfd上的读事件。监听集合采用轮询方式查看开启监听的socket上是否存在就绪的IO。
2)用户A在时刻1向服务器(serverfd)发送连接请求,读事件触发,select监听到,返回这一轮监听的就绪数ready,现在只有1个serverfd就绪,返回1。假如select监听10个,然后一轮监听返回8,这其中有1个serverfd就绪,7个clientfd就绪,serverfd永远只有一个。就绪集合,判断就绪,传入监听集合,传出就绪集合。通过就序集合判断就绪,当socket就绪时,内核将就绪集合中就绪的socket对应位保留为1,其他所有未就绪的清0。(注意传入传出参数分离)遍历就序集合,知道就绪集合中serverfd的对应位为1,判断serverfd就绪,调用accept模块建立新连接,accept返回新的clientfd,将新的clientfd加入到监听集合中,对应用户A。定义clientfd_array[] 存放 clientfd。
3)用户A在时刻2向服务器发送数据,实际上就是向它对应的clientfd(A)上发送数据,我们select也正在监听client(A),一轮监听后返回1,就绪集合中client(A)的对应位保留为1,其余都为0。serverfd只有一个,判断就绪后就可以直接建立连接,clientfd就绪后需要判断是哪个clientfd就绪,然后进行业务处理。遍历clientfd_array[]数组与就绪集合中的就绪clientfd匹配,判断到底是哪个clientfd就绪。无论哪种就绪事件处理完成后,都需要将ready- -,当ready等于0时需要开启新一轮的监听。
5、SELECT函数接口

#include <sys/select.h>
fd_set set; //监听集合类型 ,1024的监听限制源于fd_set。
FD_ZERO(fd_set * set); //初始化监听集合, 将所有的监听项初始化为0。
FD_SET(int sockfd , fd_set * set); //将某个监听集合中,特定的socket对应位置1 , 设置socket监听。
FD_CLR(int sockfd , fd_set * set); //将某个监听集合中 , 特定的socket对应位置0 , 取消socket监听。
FD_ISSET(int sockfd , fd_set * set); //判断某个监听集合中,某个socket位是0还是1并直接返回, 如果该函数结合就绪监听集合使用就可以判断该socket位是否就绪

select开启轮询监听socket事件

int ready = select(int nfds , fd_set * readfds , fd_set * wirtefds , fd_set * errfds , strcut timeval * timeout)
//参数介绍:
argv1 = nfds , 此轮监听的socket监听数量, 一般传入最大的文件描述符+1 , maxfd + 1
argv2 = readfds , 如果将监听集合传入进来,表示监听该集合中所有socket读事件,传NULL表示不监听
argv3 = writefds , 如果将监听集合传入进来,表示监听该集合中所有socket写事件,传NULL表示不监听
argv4 = errfds , 如果将监听集合传入进来,表示监听该集合中所有socket异常事件,传NULL表示不监听

注意:为什么是最大的文件描述符+1 :监听集合中前三个监听固定,分别为:STDIN(0)、STDOUT(1)、STDERR(2) ,假如接下来为serverfd(3)和clientfd1(4),那么只需要传clientfd1的文件描述符4+1=5,就可以监听这五个。

argv5 = timeout , 可以选择三种select工作模式(阻塞/非阻塞/定时阻塞)
//timeout参数的结构体类型, 根据情况设置可改变select工作模式
struct timeval
{
      senconds = 0//秒
      msenconds = 0//微妙
}tv;

timeout = NULL; 表示select采用阻塞模式监听,无就绪一直等待。
timeout = tv; 定义时间结构体,并初始化成0 ,表示select采用非阻塞模式监听,无就绪立即返回。
timeout = tv; 定义事件结构体 ,根据需求设置时间(秒/微妙),表示select采用定时阻塞, 在固定时间阻塞等待, 到时后立即返回。
select的返回值:
SELECT监听成功, 返回就绪数量readycode。
如果SELECT因非阻塞返回, 返回值为0。
SELECT返回-1 ,表示select函数调用失败。

支持简单的一对多处理,但是并不支持并发,只是能实现一对多处理效果。由于单进程,建立连接和处理业务不能同时进行,也不能同时处理多个业务,但是可以一对多处理多个客户端的业务。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值