概述
NIO(Non-blocking I/O)是Java提供的一种非阻塞IO操作方式,主要用于提高应用程序的并发性能。常用于开发高性能的服务器程序,允许单个线程管理多个网络连接,通过选择器(Selector)来监控多个通道(Channel)的状态。
- channel 有一点类似于 stream,它就是读写数据的双向通道,可以从 channel 将数据读入 buffer,也可以将 buffer 的数据写入 channel。
- buffer 则用来缓冲读写数据
- 选择器Selector则是用来管理多个连接,当连接中发生事件时,做出响应。
基本运行逻辑
Selector可以检测四种类型的IO事件,这些事件分别对应于SelectionKey的四种状态:
- 可读(OP_READ) : 当通道(Channel)处于可读状态时,表示有数据可从通道读取。对于
SocketChannel,这意味着新的数据可以被读取。 - 可写(OP_WRITE) : 当通道处于可写状态时,表示数据可以被写入通道。对于
SocketChannel,这意味着可以向连接的服务器发送数据。 - 连接就绪(OP_CONNECT) : 当一个非阻塞的
SocketChannel连接到服务器时,如果连接尚未建立,该事件表示连接操作已经完成,并且连接已经建立。这通常用于客户端初始化连接时。 - 接受连接(OP_ACCEPT) : 当一个
ServerSocketChannel处于可接受状态时,表示新的连接已经到达,服务器可以调用accept()方法来接受新的连接。这通常用于服务器端准备接受新的客户端连接。基本运行逻辑:
- 创建一个SocketServerChannel,用来接收客户端的连接
ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.bind(new InetSocketAddress(8080));
ssc.configureBlocking(false); // 将channel设置成非阻塞模式
- 创建Selector,将创建好的channel交给Selector管理,Selector会为每一个注册的channel生成一个key,往后通过该key来获取channel。
//创建Selector
Selector selector = Selector.open();
//将channel交给selector管理
ssc.register(selector, SelectionKey.OP_ACCEPT);
- 客户端事件发生,Selector会将发生事件对应的key放入SelectedKeys集合。(注意,Selector只会向集合中添加key,不会删除key,所以要手动删除,所以要用迭代器遍历)
while (true) {
int count = selector.select();
// 获取所有事件
Set < SelectionKey > keys = selector.selectedKeys();
// 遍历所有事件,逐一处理
Iterator < SelectionKey > iter = keys.iterator();
while (iter.hasNext()) {
SelectionKey key = iter.next();
// 判断事件类型
if (key.isAcceptable()) {
ServerSocketChannel c = (ServerSocketChannel) key.channel();
// 必须处理
SocketChannel sc = c.accept();
log.debug("{}", sc);
}
// 处理完毕,必须将事件移除
iter.remove();
}
}
注意:
selector.select();只会在有事件发生的时候继续运行,否则会阻塞。- 事件发生后,要么处理,要么取消(cancel),不能什么都不做,否则下次该事件仍会触发,这是因为 nio 底层使用的是水平触发