我们之前采用的多进程方式实现的服务器端,一次创建多个工作子进程来给客户端提供服务。其实这种方式是存在问题的。
可以打个比方:如果我们先前创建的几个进程承载不了目前快速发展的业务的话,是不是还得增加进程数?我们都知道系统创建进程是需要消耗大量资源的,所以这样就会导致系统资源不足的情况。
那么有没有一种方式可以让一个进程同时为多个客户端端提供服务?
接下来要讲的IO复用技术就是对于上述问题的最好解答。
对于IO复用,我们可以通过一个例子来很好的理解它。(例子来自于《TCP/IP网络编程》)
某教室有10名学生和1名老师,这些学生上课会不停的提问,所以一个老师处理不了这么多的问题。那么学校为每个学生都配一名老师,
也就是这个教室目前有10名老师。此后,只要有新的转校生,那么就会为这个学生专门分配一个老师,因为转校生也喜欢提问题。如果把以上例子中的学生比作客户端,那么老师就是负责进行数据交换的服务端。则该例子可以比作是多进程的方式。
后来有一天,来了一位具有超能力的老师,这位老师回答问题非常迅速,并且可以应对所有的问题。而这位老师采用的方式是学生提问前必须先举手,确认举手学生后在回答问题。则现在的情况就是IO复用。
目前的常用的IO复用模型有三种:select,poll,epoll。
select模型:
说的通俗一点就是各个客户端连接的文件描述符也就是套接字,都被放到了一个集合中,调用select函数之后会一直监视这些文件描述符中有哪些可读,如果有可读的描述符那么我们的工作进程就去读取资源。PHP 中有内置的函数来完成 select 系统调用。(监视文件描述符是否有可读的资源,有就读取资源)
函数原型:
int socket_select (array &KaTeX parse error: Expected 'EOF', got '&' at position 13: read ,array &̲write ,array &$except ,int $tv_sec [,int $tv_usec= 0 ])
作用说明:用于确定一个或多个套接字的状态,对每一个套接字,调用者可查询它的可读性、可写性及错误状态信息
参数说明:
read: 指向一组等待可读性检查的套接字
write: 指向一组等待可写性检查的套接字
except: 指向一组等待错误检查的套接字
tv_sec: 用来设置 select() 的等待时间,秒
tv_usec: 用来设置 select() 的等待时间,微妙
这里注意一下,如果 tv_sec 设置为0,则 socket_select 立即返回,也就是非阻塞的。如果 tv_sec 设置为 null ,则 socket_select 将一直阻塞到有套接字满足条件。
下面通过代码代码来简单举例:
poll模型:
poll 和 select 的实现非常类似,本质上的区别就是存放 fd 集合的数据结构不一样。select 在一个进程内可以维持最多 1024 个连接,poll 在此基础上做了加强,== 可以维持任意数量的连接。==
- select 和 poll 缺点
select 是通过轮训的方式来查找是否可读或者可写,打个比方,如果同时有100万个连接都没有断开,而只有一个客户端发送了数据,所以这里它还是需要循环这么多次,造成资源浪费。
所以后来出现了 epoll系统调用。
epoll模型:
epoll 是 select 和 poll 的增强版,epoll 同 poll 一样,文件描述符数量无限制。
epoll是基于内核的反射机制,在有活跃的 socket 时,系统会调用我们提前设置的回调函数。而 poll 和 select 都是遍历。
的处理函数