一、IO模型
同步、异步:
阻塞、非阻塞:
read的两步操作:
(1)等待数据准备。
(2)数据从内核空间拷贝到用户空间。
二、Unix提供的5种IO模型
(1)阻塞 IO:调用了某个函数,等待该函数返回,期间什么都不做,一直检查该函数有没有返回。等到该函数有返回才可以进行下一步动作。
实现步骤
编程步骤:
服务端:
- 创建ServerSocket实例
- 绑定占用端口
- 通过accept()方法监听并等待客户端的连接
- 如果有客户端连接,则获得Socket实例
- 通过Socket实例进行读写操作
- 关闭资源和Socket
客户端:
- 创建Socket实例
- 通过connect()方法连接服务端
- 进行读写操作
- 关闭资源
(2)非阻塞IO:非阻塞等待,每隔一段时间就去检测IO事件是否就绪。没有就绪就可以做其他事。
实现步骤:
服务端:
- 创建ServerSocketChannel实例(open)
- 对实例绑定端口(bind)
- 将实例设置为非阻塞(configureBlocking)
- 实例化selector选择器(open)
- 将ServerSocketChannel实例注册(register)到selector选择器,并关注可接受事件
- 选择器进行监听,有事件发生则返回
- 遍历感兴趣事件集合,判断是否有可接受事件
- 有可接受事件发生,获取对应通道,调用accept()方法获取SocketChannel实例
- SocketChannel实例设置为非阻塞,将其注册到选择器,并关注读事件
- 循环第6步,遍历集合,判断是否有可读事件发生
- 通过Buffer从channel读取数据
- 关闭打开的资源,包括selector选择器,关闭SocketChannel实例、ServerSocketChannel实例
客户端:
- 创建socketChanel实例
- 将socketChanel实例设置为非阻塞
- 创建selector实例
- 连接服务端,立即返回结果不成功,将chanel注册到选择器中,并关注可连接事件
- selector实例监听事件,有事件发生则返回
- 如该事件是可连接事件,则完成当前连接(finishConnect)
- 发送消息,接收消息
- 关闭资源
(3)IO复用:inux用select/poll函数实现IO复用模型,这两个函数也会使进程阻塞,但是和阻塞IO所不同的是这两个函数可以同时阻塞多个IO操作。而且可以同时对多个读操作、写操作的IO函数进行检测。知道有数据可读或可写时,才真正调用IO操作函数
(4)信号量:信号驱动IO:linux用套接口进行信号驱动IO,安装一个信号处理函数,进程继续运行并不阻塞,当IO时间就绪,进程收到SIGIO信号。然后处理IO事件。
(5)异步IO:linux中,可以调用aio_read函数告诉内核描述字缓冲区指针和缓冲区的大小、文件偏移及通知的方式,然后立即返回,当内核将数据拷贝到缓冲区后,再通知应用程序。
三、区别
(1)BIO
BIO编程步骤:
服务端:
1、创建ServerSocket实例
2、绑定占用端口
3、通过accept()方法监听并等待客户端的连接
4、如果有客户端连接,则获得Socket实例
5、通过Socket实例进行读写操作
6、关闭资源和Socket
客户端:
1、创建Socket实例
2、通过connect()方法连接服务端
3、进行读写操作
4、关闭资源
可能阻塞的方法:accept()、connect()、read()、write()
BIO缺点:
本地操作系统占用,每次创建线程。多线程时线程切换,当瞬间并发量很大时,CPU或内存使用率高。
(2)NIO(同步非阻塞)
类似于IO复用模型,包括Channel(通道)、Buffer(缓存)、Selector(选择器/IO复用器)。
NIO编程步骤:
TCP:
ServerSocketChannel
SocketChannel
服务端:
1、创建ServerSocketChannel实例(open)
2、对实例绑定端口(bind)
3、将实例设置为非阻塞(configureBlocking)
4、实例化selector选择器(open)
5、将ServerSocketChannel实例注册(register)到selector选择器,并关注可接受事件
6、选择器进行监听,有事件发生则返回
7、遍历感兴趣事件集合,判断是否有可接受事件
8、有可接受事件发生,获取对应通道,调用accept()方法获取SocketChannel实例
9、SocketChannel实例设置为非阻塞,将其注册到选择器,并关注读事件
10、循环第6步,遍历集合,判断是否有可读事件发生
11、通过Buffer从channel读取数据
12、关闭打开的资源,包括selector选择器,关闭SocketChannel实例、ServerSocketChannel实例
客户端:
1、创建socketChanel实例
2、将socketChanel实例设置为非阻塞
3、创建selector实例
4、连接服务端,立即返回结果不成功,将chanel注册到选择器中,并关注可连接事件
5、selector实例监听事件,有事件发生则返回
6、如该事件是可连接事件,则完成当前连接(finishConnect)
7、发送消息,接收消息
8、关闭资源
(3)AIO(异步阻塞IO):
accept()方法:
BIO、NIO、AIO应用场景:
| BIO | 并发量固定的业务请求。 |
| NIO | 并发量高、业务逻辑简单(轻量级)——聊天 |
| AIO | 并发量高、业务逻辑复杂(重量级) |