JAVA网络IO之简单聊聊Stream、Channel和零拷贝

发布时间:2024年01月22日

定义:Channel表示IO源与目标打开的连接。类似于传统的"流",但是channel本身不能直接访问数据,只能与buffer进行交互。channel本身不存储数据,因此需要配合缓冲区进行传输

channel与Stream流对文件的读写

channel对文件的读写

        try {
            FileInputStream fileInputStream = new FileInputStream("D:\\books.txt");
            FileChannel fileChannel = fileInputStream.getChannel(); // 打开一个通道
            // 面向缓冲区编程 定义一个缓冲区
            ByteBuffer byteBuffer = ByteBuffer.allocate(10); // 定义缓冲区字节数的大小
            int read = fileChannel.read(byteBuffer);   // channel不直接访问数据 而是从缓冲区中 读数据
            System.out.println(new String(byteBuffer.array()));

            // 写数据
            FileOutputStream fileOutputStream = new FileOutputStream("D:\\books_cp.txt");
            FileChannel outputStreamChannel = fileOutputStream.getChannel();
//            ByteBuffer buffer = ByteBuffer.allocate(10); // 定义缓冲区字节数的大小
//            buffer.put("Hello NIO".getBytes()); // 往缓冲区写数据
            byteBuffer.flip();  // 把读模式转换成写操作
            outputStreamChannel.write(byteBuffer);

        } catch (FileNotFoundException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

传统Stream对文件的读写

在这里 读的操作使用到了缓冲流,具体关于传统stream流的写法可以参考博客:JAVA IO流之基础IO-CSDN博客

      // 基于磁盘文件的字节输入输出流
        //1.将磁盘文件使用Input流输入到内存  然后再写入到磁盘文件中
        FileInputStream inputStream = new FileInputStream("D:\\books.txt");
        BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream);
        FileOutputStream fileOutputStream = new FileOutputStream("D:\\hello1.txt");
        int i = 0;
        while ((i = bufferedInputStream.read()) != -1) {
            fileOutputStream.write(i);
        }
        inputStream.close();
        fileOutputStream.close();
        bufferedInputStream.close();

channel与stream的区别

1. 是否可以双向传输: 从上面代码可以看出来,channel读和写都是使用的FileChanel,而Stream流中读使用的是InputStream 写使用的是outputStream? 因此channel是支持双向传输的,而stream流只支持单向传输

2.是否必须面向缓冲区: channel本身不直接访问数据 因此channel必须面向缓冲区来进行数据的读写,Stream流可以直接读写数据,从代码里也可以看出,写入数据时没用到缓冲区也是可以的。

3.是否支持异步:channel支持异步,stream不支持异步。例如在多路复用器中,使用多路复用器selector监听chnanel时,并不需要像BIO一样,阻塞到channel有数据才返回。而是channel有数据了会主动去通知Seector,让Selector再去处理。 (BIO和多路复用器的具体用法可以参考博客JAVA IO流之网络IO-CSDN博客)

零拷贝

什么是零拷贝?

一个文件的读写的正常交互逻辑是怎么样的?可以先看下面的一幅图

第一步:向磁盘发出请求,我要读你的数据了

第二步:把磁盘上的文件 写入到内核的一个缓冲区

第三步:由内核的缓冲区在复制一份到用户空间的缓冲区,就是最后我们可以看到的

那么什么是零拷贝?零拷贝就是对这一个过程的优化,将内核空间的缓冲区和用户空间的缓冲区构造一个缓冲区,那么就相当于把第二步和第三步融合在了一起,提高效率。

零拷贝文件的实现

        try {
            FileChannel inChannel = FileChannel.open(Paths.get("D:\\books.txt"), StandardOpenOption.READ);
            FileChannel outChannel = FileChannel.open(Paths.get("D:\\bookes.txt"), StandardOpenOption.READ, StandardOpenOption.CREATE, StandardOpenOption.WRITE);

            MappedByteBuffer inMappedByteBuffer = inChannel.map(FileChannel.MapMode.READ_ONLY, 0, inChannel.size());
            MappedByteBuffer outMappedByteBuffer = outChannel.map(FileChannel.MapMode.READ_WRITE, 0, inChannel.size());

            byte[] bytes = new byte[inMappedByteBuffer.limit()];

            inMappedByteBuffer.get(bytes);
            outMappedByteBuffer.put(bytes);

            inChannel.close();
            outChannel.close();

        } catch (IOException e) {
            throw new RuntimeException(e);
        }

分析: 利用mmap虚拟的一个共享内存,从上面代码 通道channel只跟 mmapbuffer这一个缓冲区打交道,直接从缓冲区中取出数据来进行写入,也可以理解为省去了用户空间的部分。

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