这里写目录标题
- 引入
- 场景
- 多进程并发服务器
- 二级目录
- 二级目录
- 二级目录
- 多线程并发服务器
- 二级目录
- 二级目录
- 二级目录
- 多路IO转接服务器
- 设计思路
- 对比
- 引入
- select函数简介
- 参数介绍
- 第一个参数
- 第234参数
- 返回值
- 对于第234参数的应用
- 对于最后一个参数
- 总结
- epoll进阶
- 二级目录
- 二级目录
- 二级目录
- 一级目录
- 二级目录
- 二级目录
- 二级目录
引入
场景
现在我们有了服务端和客户端(上图均以终端代替)
我们从客户端连接上服务端,可以进行正常的运行,但是如果此时第二个客户端也连接上服务端的话,第二个客户端是无法正常运行的,因为目前我们只能处理单进程,一对一的服务提供,无法进行多个客户端同时连接,所以,就有了高并发服务器
多进程并发服务器
二级目录
二级目录
二级目录
多线程并发服务器
二级目录
二级目录
二级目录
多路IO转接服务器
设计思路
对比
首先如上图,我们可以看到,之前的多进程并发和多线程并发的设计思路是:
首先服务端会创建一个监听套接字 listen,即lfd,之后该套接字会一直处于阻塞监听状态,循环调用Accept函数,一旦发现有客户端连进来,就生成一个用于连接的cfd套接字,与客户端建立连接,之后lfd再次处于监听,一旦有其他客户端,就让服务端再创建一个用于连接的cfd套接字,去与客户端2连接,…
但是如果这样的话,服务端的压力会很大,服务端会一直处于阻塞监听,所以,我们就引入了多路IO转接服务器
引入
我们不再使用服务器阻塞监听了,假设我们的服务端是老板,他会雇一个秘书select(由内核实现,我们无需实现),负责监听客户端,首先服务端生成一个lfd监听套接字,然后交给select,select通过lfd来监听客户端,一旦有客户端发起连接请求,会先经过select,之后select接到连接请求之后,将连接请求报告给服务端,服务端只有接到select的通知,才会创建出一个cfd与请求的客户端建立连接,而且这时是确定有客户端要来连接,所以,不用阻塞,直接进行创建然后连接即可,之后把该cfd交给select来管理。
所以,如上图所示的例子的话,select有四个监听,一个负责监听是否有新的客户端来请求连接,其他三个负责监听接收客户端那边发来的信息,反馈给服务端,服务端再将处理完后的结果给到select,select再返回给客户端
这样的机制就叫做响应模式,客户端来请求,服务端才会进行连接,学名叫多路IO转接
而不同的秘书,有着不同的功能,除了select之外,还有俩秘书,分别是poll和epoll
select函数简介
参数介绍
第一个参数
首先,在上图左侧是一个文件描述符表,我们要知道,文件描述符是有编号的,他们会收录在文件描述符表中,首先0 1 2 是被占用的,所以,我们自己的文件描述符会从3开始,如上图所示例子的话,我们最大的文件描述符编号是6,但是我们要传入6+1,因为他的内部是一个for循环,循环所有的文件描述符,而循环条件是<,所以应该+1
第234参数
他们的类型都是文件描述符集合的指针,他们都是传入传出参数,即可以从参数传出内容至函数外(因为他们都是指针)
对于第二个参数,形参名字是readfds,从名字我们也能知道,他是负责读事件的,实际上,他是负责管理select监听哪几个套接字的读事件,他是一个文件描述符集合,实际上就是套接字集合,因为一个文件描述符对应一个套接字,如上图r,他的集合里是3 5 6,那说明他监听3号5号6号套接字的读事件(读是站在select的视角来说的,也就是select在读取内容(即客户端发送内容给select))。但是只是监听,是否真正发送读事件还是另一回事
如法炮制,第3、4个参数分别是,站在select的视角来说,监听哪些文件描述符的写事件、以及异常事件
返回值
因为第234都是传入传出参数,他们的返回值还是一个文件描述符集合,返回那些真正执行了对应事件的文件描述符集合(因为传入的只是监听集合,他们是否真正发生对应的事件并不要求)
select函数的返回值是这三个集合返回的文件描述符总数
对于第234参数的应用
他们是文件描述符集合,实际上底层是一个位图,其中下标分别对应不同编号的文件描述符,而他的值只有0、1,所以对于传入readfds来说,只有3、5、6是1,其他都是0,而对于传出readfds来说,只有5、6是1。
因为底层是位图,所以我们肯定会做一些位操作,那么位操作也有对应的函数,如下图
对于最后一个参数
总结