NIO的实战教程(简单且高效)

2023-12-20 16:30:07

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方法创建一个Selector
            Selector 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不阻塞的原因

文章来源:https://blog.csdn.net/m0_46638350/article/details/135009064
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。