IO流程示意图
客户端发送数据来到服务器,建立好sokcet连接再到应用程序拿到这个数据可以分为两个步骤
因为应用程序是没办法直接去网卡读取数据的,应用程序向OS内核发起read调用
1.数据准备(输入流从网卡来到OS内核空间)
2.数据拷贝(从内核空间拷贝到用户空间)
根据应用程序接收结果的通知方式分为 同步和异步。简单理解:同步就是应用程序发起read调用之后就一直等待结果,异步就是应用程序先去做其他事,等待内核程序
根据调用者(client)的影响分为 :阻塞和非阻塞。
两类情况总共组合出5种IO模型:
先看阻塞IO
当有多个客户端都发来数据,由于都建立好了socket连接 由于应用程序在第一个client发起read调用的时候已经阻塞等待结果(recvFrom(block)),那么就无法处理第二个client的socket发来的数据流,也就是一个线程只能接收一个client的处理,一个处理完了才能处理下一个。
非阻塞IO
对于一个线程来说
是一个循环发起accprt()和read()的方法accpt和read()方法都不会阻塞,这样应用程序维护了一个
非阻塞IO太消耗CPU了 不断轮询socket
Java中的NIO没有采用非阻塞IO 而是采用IO多路复用
由eopll函数去监听多个socket 那个有数据流请求 ,对于应用程序的线程来说,多路复用器Selector 调用select()其实就是调用OS中的eopll()方法以及从数据从内核空间拷贝到用户空间仍然是阻塞,但是对于客户端来说是非阻塞的,只需要把socket个epoll去监听即可
结合代码看更方便
所有的事件都抽象成selectionsKey
重点就是在事件轮询器selector(多路复用器)和事件上 这样一个线程通过selector就能监听多个客户单的连接,这样相比bio,大大降低了服务端面对并发请求的开销,也即是IO多路复用的含义
但此时 这种nio编程的方式还是有缺陷
如果去遍历事件 然后去处理,如果有一个业务数据拷贝事件过长了怎么办?解决方法也很简单,将handle多开一个工作线程去处理,于是引出了Reactor模型,详见
最后关于epoll函数可以参考这篇文章,讲的非常好
https://zhuanlan.zhihu.com/p/63179839