4种网络IO模型

4种网络IO模型

当一个网络*IO*发生时,它会涉及到两个系统对象,一个是调用这个IO的进程,另一个是系统内核。当一个read操作发生时,它会经历两个阶段:(1)等待数据准备;(2)将数据从内核拷贝到进程。

为了解决网络IO问题,有四种网络IO模型:(1)阻塞IO模型(2)非阻塞IO模型(3)多路复用IO模型(4)异步IO模型

阻塞IO模型

在Linux中,默认情况下所有的socket都是阻塞的,一个读操作流程如图1所示

4种网络IO模型_第1张图片
阻塞和非阻塞的概念描述的是用户线程调用内核IO操作方式:阻塞是指IO操作需要彻底完成后才能返回用户空间;而非阻塞是指IO操作被调用后立即返回给用户一个状态值,不需要等到IO操作彻底完成。
大部分的socket接口都是阻塞型的。所谓阻塞型指系统调用时,却不返回调用结果,并让当前线程一直处于阻塞状态,只有当该系统调用获得结果或者超时出错时才返回结果。除非特别指定,几乎所有IO接口都是阻塞型的。这个网络编程带来很大的问题,如在调用send()的同时,线程处于阻塞状态,则在此期间,线程将无法执行任何运算或响应任何网络请求。
一个简单的改进方案是在服务器端使用多线程(或多进程)。多线程(或多进程)的目的是让每个连接都有独立的线程(或进程),这样任何一个连接的阻塞都不会影响其他连接。如果同时响应成百上千路的连接请求,则无论多线程还是多进程都会严重占用系统资源,降低系统对外界响应的效率,而线程与进程本身也容易进入假死状态。很多程序员会考虑使用“线程池”或“连接池”。“线程池”旨在降低创建和销毁线程的频率,使其维持一定合理数量的线程,并让空闲的线程重新承担新的执行任务。“连接池”指维持连接的缓存池,尽量重用已有的连接,降低创建和关闭连接的频率。但是,“线程池”和“连接池”只是在一定程度上缓解了频繁调用IO接口带来的资源占用。而且“池”的大小是有限制的。面对大规模的服务请求,多线程模型也会遇到瓶颈,可以用非阻塞模型尝试解决这个问题。

非阻塞模型

在Linux下,可以通过设置socket使IO变为非阻塞。对一个非阻塞的socket执行read操作时,流程如图2

4种网络IO模型_第2张图片
从图中可以看出,用户进程发出read操作时,如果内核中的数据还没有准备好,它不会block用户进程,而是立刻返回一个错误。从用户的角度,它发起一个read操作后,并不需要等待,而是马上得到一个结果。
服务器线程通过循环调用recv()接口,可以在单个线程内实现对所有连接数据的接收工作。但是因为循环调用recv()将大幅度占用CPU使用率,此外,recv()更多的是起到“操作是否完成”的作用,实际操作系统提供了更高效的检测“操作是否完成”作用的接口,例如select()多路复用模式,可以一次检测到多个连接是否活跃。

多路IO复用模型

多路IO复用,也称事件驱动IO。它的基本原理就是有个函数(如select)会不断地轮询所负责的所以socket,当某个socket有数据到达了,就通知用户进程。多路IO复用模型的流程如图3所示

4种网络IO模型_第3张图片
这个模型和阻塞IO的模型其实没有太大区别,事实上还更差一些。因为它需要两个系统调用(select 和 recvfrom),而阻塞IO只调用了一个系统调用(recvfrom)。但是,用select 的优势在于它可以同时处理多个连接。

异步IO模型

异步IO模型的流程图如图4所示

4种网络IO模型_第4张图片
用户进程发起read操作之后,立刻就可以开始去做其他的事,从内核的角度,当它收到一个异步的read请求操作之后,首先会立刻返回,所以不会对用户进程产生任何阻塞。
各种IO模型的比较
4种网络IO模型_第5张图片

你可能感兴趣的:(C++)