1. 参考
建议按顺序阅读以下三篇文章
为什么NIO被称为同步非阻塞?
Java IO 与 NIO:高效的输入输出操作探究
【Java.NIO】Selector,及SelectionKey
2. 实战
我们将模拟一个简单的HTTP服务器,它将响应客户端请求并返回一个固定的响应(”Hello, World!”)。我们将使用IO和NIO两种不同的方式实现此服务器。
2.1 传统阻塞IO
import java.io.*;
public class TraditionalIOExample {public static void main(String[] args) {try {// 打开文件InputStream input = new FileInputStream("example.txt");OutputStream output = new FileOutputStream("output.txt");// 读取和写入数据int data;while ((data = input.read()) != -1) {output.write(data);}// 关闭文件input.close();output.close();} catch (IOException e) {e.printStackTrace();}}
}
2.2 非阻塞NIO
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.Set;public class NioHttpServer {public static void main(String[] args) {try {// 创建服务端通道ServerSocketChannel serverChannel = ServerSocketChannel.open();// 绑定访问端口serverChannel.socket().bind(new InetSocketAddress(8080));// 通道设置为非阻塞serverChannel.configureBlocking(false);// 通过open方法创建一个SelectorSelector selector = Selector.open();/** 必须将channel注册到selector上,并订阅OP_ACCEPT事件SelectionKey.OP_CONNECT channel成功连接到另一个服务器称为”连接就绪“SelectionKey.OP_ACCEPT server socket channel准备好接收新进入的连接称为”接收就绪“SelectionKey.OP_READ 有数据可读的通道可以说是”读就绪“SelectionKey.OP_WRITE 有数据可写的通道可以说是”读就绪“*/serverChannel.register(selector, SelectionKey.OP_ACCEPT);while (true) {// 返回你所感兴趣的事件(连接,接受,读或写)已经准备就绪的那些通道int readyChannels = selector.select();if (readyChannels == 0){continue;}// 访问”已选择键集“中的就绪通道Set<SelectionKey> selectedKeys = selector.selectedKeys();// 可以遍历这个已选择的集合来访问就绪的通道Iterator<SelectionKey> keyIterator = selectedKeys.iterator();while (keyIterator.hasNext()) {SelectionKey key = keyIterator.next();// 注意每次迭代末尾的remove()调用,Selector不会自己从已选择集中移除SelectioKey实例,必须在处理完通道时自己移除。keyIterator.remove();// 一个server socket channel准备号接收新进入的连接称为”接收就绪“if (key.isAcceptable()) {ServerSocketChannel server = (ServerSocketChannel) key.channel();// 客户端socker注册进来SocketChannel clientChannel = server.accept();clientChannel.configureBlocking(false);clientChannel.register(selector, SelectionKey.OP_READ);// 客户端通道是否有数据流进来} else if (key.isReadable()) {SocketChannel clientChannel = (SocketChannel) key.channel();ByteBuffer buffer = ByteBuffer.allocate(1024);clientChannel.read(buffer);buffer.flip();byte[] bytes = new byte[buffer.remaining()];buffer.get(bytes);String request = new String(bytes);String response = "HTTP/1.1 200 OK\r\n\r\nHello, World!\r\n";ByteBuffer responseBuffer = ByteBuffer.wrap(response.getBytes());clientChannel.write(responseBuffer);clientChannel.close();}}}} catch (IOException e) {e.printStackTrace();}}
}
3. 模型
上述代码结合该模型,第二次阅读代码,会有更深的理解
4. 原理
多路复用才是NIO不阻塞的原因