网络编程中,我们常常接触阻塞,非阻塞,同步,异步等概念,有些概念可能交叉使用,比如异步非阻塞,同步非阻塞,同步阻塞等等,这些概念看似相似,却往往又有着不同的概念,也往往很绕。刚刚查了点资料总结了一下,通过结合具体的网络模型来给这些概念做个笔记。
阻塞是最容易理解的,对应于各种阻塞的api,recv send等等都是阻塞的API,这些API的特性就是在内核的数据没有准备好的情况下,它会阻塞整个线程,直到内核收到了这些数据,于是内核会将这部分数据复制到用户空间并返回,这样堵塞的API就返回具体的结果了。总之就是必须有结果返回,该系列的API才会返回。
非阻塞也是非常好理解的,然而结合同步异步的概念,可能会有点混淆,这里先谈下非阻塞的概念。
非阻塞系列的API,当把socket执行了设置非阻塞的socket后,recv send函数每次执行的时候就会立刻返回,而立刻返回的时候,假设没有满足用户的参数条件,比如还没有足够的数据可以读的时候,那么其实这些API会返回一个阻塞的错误,于是相比阻塞API,非阻塞后,使用者可能需要不断的调用这些API来获得对应的结果,所以非阻塞的API在没有对应的网络模型的支持下,其实是没有什么大的用处的。
同步这个概念比较难解释。首先同步的概念,也就是执行的结果必须由用户自己获得,在这里也就是用户必须自己调用recv函数或者send函数来获得操作的结果。
同步这个概念和非阻塞联合起来,很像异步的概念,比如select模型和epoll模型,然而这两个模型都是同步非阻塞模型。这两个模型都会告诉你具体哪个socket有了事件,然而用户还得自己去获取数据,也就是在非阻塞的基础上加上了一个通知机制。
异步模型主要有AIO和IOCP,这两个模型的主要特点就是告诉它们我需要读取或者发送数据,当读取数据或者发送数据完成后,它们就带着结果返回了,当然也完成了数据从内核空间到用户空间的拷贝,使用者无需再次调用recv等函数进行一次数据的拷贝。
其实是看libevent实现的时候想到的,从使用者的角度来看,libevent应该是一个proactor模型,用户要求读取数据,那么libevent会把数据存入到对应的缓冲区,用户就可以读取这些数据,非常像异步的实现方式。然而不考虑windows下iocp的封装,libevent中最常见的封装方式应该就是select和epoll,而这些模型都是同步模型,只是提供了一个通知机制而已,在libevent中也是封装了这些模型不断的去轮询有哪些socket上有事件,在实现角度上来看其实还是基于reactor。所以总结libevent,它其实是一个非阻塞的同步模型。