JavaNIO 学习

Java NIO 是指 Java New IO 类库,基于多路复用IO模型实现,分为三大核心组件:Buffer、Channel、Selector,接下来以这三大组件进行测试学习。

Buffer

从管道中读取数据时,数据流向为Channel到Buffer;往管道中写入数据时,数据流向为Buffer到Channel。可以理解为Buffer是一个临时缓冲区,用于保存临时数据,Channel用于保存真实数据。以 IntBuffer 为例子进行测试:

    public static IntBuffer intBuffer;

    /**
     * 初始化 IntBuffer
     */
    public static void init() {
        intBuffer = IntBuffer.allocate(10);
        System.out.println("init done.");
    }

    /**
     * 打印 IntBuffer 参数
     */
    public static void display() {
        System.out.println("================= Java NIO Buffer Display =================");
        System.out.println("limit " + intBuffer.limit());
        System.out.println("position " + intBuffer.position());
        System.out.println("capacity " + intBuffer.capacity());
    }

    /**
     * 往 IntBuffer 写入数字
     */
    public static void write() {
        System.out.println("================= put 5 int num into intBuffer =================");
        for (int i = 0; i < 5; i++) {
            intBuffer.put(i);
        }
        System.out.println("write done.");
    }

    /**
     * 从 IntBuffer 中读取数据
     */
    public static void read() {
        System.out.println("================= read 5 int num from intBuffer =================");
        for (int i = 0; i < 5; i++) {
            System.out.println(intBuffer.get());
        }
        System.out.println("read done.");
    }

    public static void main(String[] args) {

        init();
        display();

        write();
        display();

        intBuffer.flip();
        display();

        read();
        display();

        intBuffer.clear();
        display();
    }

输出如下:

init done.
================= Java NIO Buffer Display =================
limit 10
position 0
capacity 10
================= put 5 int num into intBuffer =================
write done.
================= Java NIO Buffer Display =================
limit 10
position 5
capacity 10
================= Java NIO Buffer Display =================
limit 5
position 0
capacity 10
================= read 5 int num from intBuffer =================
0
1
2
3
4
read done.
================= Java NIO Buffer Display =================
limit 5
position 5
capacity 10
================= Java NIO Buffer Display =================
limit 10
position 0
capacity 10

Channel

以 ByteBuffer 为例子,实现简单的文件复制:

    /**
     * 文件复制测试
     * @param srcFileName
     * @param destFileName
     */
    public static void copyFile(String srcFileName, String destFileName) {
        FileInputStream fileInputStream = null;
        FileOutputStream fileOutputStream = null;
        FileChannel inputStreamChannel = null;
        FileChannel outputStreamChannel = null;
        try {
            // 获取文件输入输出流
            fileInputStream = new FileInputStream(srcFileName);
            fileOutputStream = new FileOutputStream(destFileName);

            // 获取管道
            inputStreamChannel = fileInputStream.getChannel();
            outputStreamChannel = fileOutputStream.getChannel();

            // 创建缓存区
            ByteBuffer byteBuffer = ByteBuffer.allocate(1024);

            // 从管道中读取数据到缓存区
            while (inputStreamChannel.read(byteBuffer) != -1) {
                // 缓存区从写模式转成读模式
                byteBuffer.flip();
                while (outputStreamChannel.write(byteBuffer) != 0) {

                }
                byteBuffer.clear();
            }
            // 强制刷新
            outputStreamChannel.force(true);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            close(fileInputStream);
            close(fileOutputStream);
            close(inputStreamChannel);
            close(outputStreamChannel);
        }

    }

更高效的方式应该为使用管道的 transferFrom 方法:

    public static void copyFileFast(String srcFileName, String destFileName) {
        FileInputStream fileInputStream = null;
        FileOutputStream fileOutputStream = null;
        FileChannel inputStreamChannel = null;
        FileChannel outputStreamChannel = null;
        long startTime = System.currentTimeMillis();
        try {
            // 获取文件输入输出流
            fileInputStream = new FileInputStream(srcFileName);
            fileOutputStream = new FileOutputStream(destFileName);

            // 获取管道
            inputStreamChannel = fileInputStream.getChannel();
            outputStreamChannel = fileOutputStream.getChannel();

            long size = inputStreamChannel.size();
            System.out.println("size: " + size);
            long position = 0;
            long count;
            while (position < size) {
                // 每次最多复制 1024000000 byte = 1 GB
                count = size - position > 1024000000 ? 1024000000 : size - position;
                // 复制内存,偏移量position + count长度
                position = position + outputStreamChannel.transferFrom(inputStreamChannel, position, count);
                System.out.println("position: " + position);
            }
            // 强制刷新
            outputStreamChannel.force(true);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            close(fileInputStream);
            close(fileOutputStream);
            close(inputStreamChannel);
            close(outputStreamChannel);
        }

        long endTime = System.currentTimeMillis();
        System.out.println("所需时间:" + (endTime - startTime));
    }

输出如下:

size: 4781506560
position: 1024000000
position: 2048000000
position: 3072000000
position: 4096000000
position: 4781506560
所需时间:187682

Selector

通过 Selector 可以同时监听多个 Channel ,以 SocketChannel 和 serverSocketChannel 为例子实现简单的客户端/服务器通讯:

/**
 * @Description: 基于 NIO 的 CS 测试
 * @Author: Jiansheng Ma
 * @Date: 2022/04/30/20:00
 */
public class SelectorTest {

    public static void main(String[] args) {

        try {
            // 服务端就绪
            new Server("Server1").listenAndRun();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

/**
 * 客户端,负责向服务端发送数据
 */
class Client {
    private String clientName;

    public Client(String clientName) {
        this.clientName = clientName;
    }

    public void sendData() throws IOException {
        InetSocketAddress address = new InetSocketAddress("127.0.0.1", 12345);
        SocketChannel socketChannel = SocketChannel.open(address);
        socketChannel.configureBlocking(false);
        while (!socketChannel.finishConnect()) {

        }
        // 发送数据
        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
        byteBuffer.put(this.clientName.concat(": ").concat(LocalDateTime.now().toString()).getBytes());
        byteBuffer.flip();
        socketChannel.write(byteBuffer);
        socketChannel.shutdownOutput();
        socketChannel.close();
    }
}

/**
 * 服务端,负责接受客户端数据并打印
 */
class Server {

    private String serverName;

    public Server(String serverName) {
        this.serverName = serverName;
    }

    public void listenAndRun() throws IOException {
        InetSocketAddress address = new InetSocketAddress(12345);
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.bind(address);
        serverSocketChannel.configureBlocking(false);
        Selector selector = Selector.open();
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        while (selector.select() > 0) {
            Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
            while (iterator.hasNext()) {
                SelectionKey next = iterator.next();
                if (next.isAcceptable()) {
                    SocketChannel socketChannel = serverSocketChannel.accept();
                    socketChannel.configureBlocking(false);
                    socketChannel.register(selector, SelectionKey.OP_READ);
                } else if (next.isReadable()) {
                    SocketChannel socketChannel = (SocketChannel) next.channel();
                    // 读取数据
                    ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
                    while (socketChannel.read(byteBuffer) > 0) {
                        byteBuffer.flip();
                        System.out.println(new String(byteBuffer.array()));
                        byteBuffer.clear();
                    }
                    socketChannel.close();
                } else {
                    System.out.println("other ops");
                }
            }
            iterator.remove();
        }
        selector.close();
    }
}


public class Test {

    public static void testSocket() {

        new Thread(() -> {
            try {
                Client clientB = new Client("ClientB");
                while (true) {
                    clientB.sendData();
                    Thread.sleep(2000);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }).start();
    }

    public static void main(String[] args) {
        testSocket();
    }
}

先开启服务端,再开启两个客户端,服务端输出如下:

ClientA: 2022-05-02T17:56:22.606                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                
ClientA: 2022-05-02T17:56:24.608                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                
ClientB: 2022-05-02T17:56:24.640                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                
ClientA: 2022-05-02T17:56:26.616                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                
ClientB: 2022-05-02T17:56:26.647                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                
ClientA: 2022-05-02T17:56:28.628                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                
ClientB: 2022-05-02T17:56:28.660                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                
ClientA: 2022-05-02T17:56:30.643                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                
ClientB: 2022-05-02T17:56:30.674                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                
ClientA: 2022-05-02T17:56:32.650                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                
ClientB: 2022-05-02T17:56:32.682                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                

关闭服务端后,客户端提示连接错误:

java.net.ConnectException: Connection refused: connect
	at sun.nio.ch.Net.connect0(Native Method)
	at sun.nio.ch.Net.connect(Net.java:454)
	at sun.nio.ch.Net.connect(Net.java:446)
	at sun.nio.ch.SocketChannelImpl.connect(SocketChannelImpl.java:648)
	at java.nio.channels.SocketChannel.open(SocketChannel.java:189)
	at Client.sendData(SelectorTest.java:41)
	at Test2.lambda$testSocket$0(Test2.java:14)
	at java.lang.Thread.run(Thread.java:745)

Process finished with exit code 0

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

余衫马

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值