-
区分
按照《Unix网络编程》的划分,IO模型可以分为:阻塞IO、非阻塞IO、IO复用、信号驱动IO和异步IO, 按照POSIX标准来划分只分为两类:同步IO和异步IO。 如何区分呢? 首先一个IO操作(read/write系统调用)其实分成了两个步骤:1)发起IO请求和2)实际的IO读写(内核态与用户态的数据拷贝) 阻塞IO和非阻塞IO的区别在于第一步,发起IO请求的进程是否会被阻塞, 如果阻塞直到IO操作完成才返回,那么就是传统的阻塞IO,如果不阻塞,那么就是非阻塞IO。 同步IO和异步IO的区别就在于第二步,实际的IO读写(内核态与用户态的数据拷贝)是否需要进程参与, 如果需要进程参与则是同步IO,如果不需要进程参与就是异步IO。 如果实际的IO读写需要请求进程参与,那么就是同步IO。因此阻塞IO、非阻塞IO、IO复用、信号驱动IO都是同步IO 在编程上,这种非阻塞IO一般都采用IO状态事件+回调方法的方式来处理IO操作。 如果是同步IO,则状态事件为读写就绪。此时的数据仍在内核态中,但是已经准备就绪,可以进行IO读写操作。 如果是异步IO,则状态事件为读写完成。此时的数据已经存在于应用进程的地址空间(用户态)中。以上全是和IO相关的同步异步。 另外在编程的方法调用上也存在同步调用和异步调用的说法。 就拿RPC来说吧:如果同步调用,则调用的结果会在本次调用后返回。 如果异步调用,则调用的结果不会直接返回。会返回一个Future或者Promise对象来供调用方主动/被动的获取本次调用的结果。
关于同步,异步,阻塞,非阻塞,IOCP/epoll,select/poll,AIO ,NIO ,BIO的总结 多路服用
-
网络IO的需求
开启一个socket,接收连接,读数据、处理数据、写数据
BIO(阻塞):新的连接分配一个线程,等待读数据,有了之后处理数据,再写数据
NIO(非阻塞):将连接放到一起,事件驱动,select/poll事件,将读数据、处理数据、写数据分开
Reactor模型终极模式,主线程select事件,读、写IO交给一个线程池,业务处理交给另一个线程池
AIO(异步IO):主动select/poll事件是同步的,异步则通过epoll注册到操作系统,等待通知 -
BIO的问题?
-
NIO解决的问题?
同步非阻塞IO,不用一直等待客户端IO输入,读写还是同步阻塞的 -
IO多路复用是复用什么
一般指一个线程或少量线程处理多个TCP连接(或Chanel),复用一个线程或少量线程 也就是reactor线程模型 -
netty线程模型
通常单reactor多worker -
select/poll,epoll区别
poll遍历数组,判断状态 epoll在操作系统实现时不需要轮询,主动通知
BIO
服务器端启动一个SeverSocket
客户端启动Socket对服务器端发起通信,默认情况下服务器端需为每个客户端创建一个线程与之通讯
客户端发起请求后,先咨询服务器端是否有线程响应,如果没有则会等待或被拒绝
如果有线程响应,客户端线程会等待请求结束后,再继续执行
//BIO-服务器端
public class BIOSever {
public static void main(String[] args) throws IOException {
//在BIO中,可以使用线程池进行优化
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
ServerSocket serverSocket = new ServerSocket(6666);
System.out.println("服务器已启动");
while (true){
System.out.println("等待客户端连接.....(阻塞中)");
Socket socket = serverSocket.accept();
System.out.println("客户端连接");
cachedThreadPool.execute(new Runnable() {
public void run() {
handler(socket);
}
});
}
}
//从客服端socket读取数据
public static void handler(Socket socket){
try{
InputStream inputStream = socket.getInputStream();
byte[] b = new byte[1024];
while (true){
System.out.println("等待客户端输入.....(阻塞中)");
int read = inputStream.read(b);
if (read != -1){
System.out.println(new String(b, 0, read));
}else {
break;
}
}
inputStream.close();
}catch (Exception e){
e.printStackTrace();
}finally {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
BIO问题分析
从上面代码中可以看出BIO编程的两个问题:
服务器端在监听客户端连接时(serverSocket.accept()),服务器端处于阻塞状态,不能处理其他事务
服务器端需要为每个客户端建立一个线程,虽然可以用线程池来优化,但在并发较大时,线程开销依旧很大
当连接的客户端没有发送数据时,服务器端会阻塞在read操作上,等待客户端输入,造成线程资源浪费