zoukankan      html  css  js  c++  java
  • 通道和缓冲区

    • 通道
    通常来说NIO中的所有IO都是从Channel开始的。Channel和流有点类似。通过Channel,我们即可以从Channel把数据写到Buffer中,也可以把数据从Buffer写入到Channel,下图是一个示意图: 

     

    通道可以理解成一种连接,根据连接对象的不同,可以分为下面这些类型

    文件连接:FileChannel,用于文件的读写

    UDP连接:DatagramChannel,用于UDP数据的读写

    Socket客户端:SocketChannel,用于TCP数据的读写

    Socket服务端:ServerSocketChanel,用于监听TCP连接,每个请求都会生成一个SocketChannel

    通道的作用相当于基本IO中的流,但有如下几点区别:

    1、通道可以读也可以写,而流只能读或者写

    2、通道可以异步读写,而流在读或写的时候,都是阻塞的

    3、通道总是基于缓冲区Buffer来读写的,而流的读写是基于虚拟机内存的

    demo01:使用通道来进行文件的读取

    
    
     1     /**
     2      * 使用通道读文件数据
     3      */
     4     public void readFile() {
     5         Path path = Paths.get("/Users/sherry/WorkPath/Git/LinkTest/zln-learning/testDir/content.html");
     6         Charset charset = Charset.forName("UTF-8");
     7         CharsetDecoder decoder = charset.newDecoder();//解码器
     8 
     9         try (FileChannel fileChannel = FileChannel.open(path, StandardOpenOption.READ)) {
    10             ByteBuffer byteBuffer = ByteBuffer.allocate((int) fileChannel.size());
    11             CharBuffer charBuffer = CharBuffer.allocate((int) fileChannel.size());
    12             logger.debug("缓冲区大小:"+(int) fileChannel.size());//371
    13             if ((fileChannel.read(byteBuffer)) != -1) {//因为缓存容量的缘故,其实只需要读取一次  对于大文件,需要循环
    14                 logger.debug("ByteBuffer limit:"+byteBuffer.limit());//371
    15                 byteBuffer.flip();//limit到当前位置  pos到0
    16                 decoder.decode(byteBuffer, charBuffer, true);//将读取到的字节按照指定方式进行解码,写入到字符缓冲区中
    17                 logger.debug("CharBuffer limit:"+charBuffer.limit());//371
    18                 charBuffer.flip();
    19                 System.out.print(charBuffer);
    20 
    21                 byteBuffer.clear();
    22                 charBuffer.clear();
    23             }
    24         } catch (IOException e) {
    25             logger.error(e.getMessage(), e);
    26         }
    27     }

    用到了如下知识

      1、Path表示路径

      2、使用CharserDecoder进行解码

      3、ByteBuffer到CharBuffer之间使用解码器进行转换

      4、使用TWR实现资源的安全关闭

      5、缓冲区的flip与clear操作

    demo02:使用通道实现文件的写入

     1     /**
     2      * 打开并写数据
     3      *
     4      * @throws IOException
     5      */
     6     public void openAndWrite() throws IOException {
     7         Charset charset = Charset.forName("UTF-8");
     8         CharsetEncoder encoder = charset.newEncoder();
     9         //已有的File流,一般也提供了 getChannel 方法,用于获取 FileChannel 实例
    10         try (FileChannel fileChannel = FileChannel.open(Paths.get("/Users/sherry/WorkPath/Git/LinkTest/zln-learning/testDir/fileChannel.txt")
    11                 , StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.WRITE)) {
    12             ByteBuffer byteBuffer = ByteBuffer.allocate(1024 * 4);
    13             CharBuffer charBuffer = CharBuffer.allocate(1024 * 4);
    14             for (int i = 0; i < 100; i++) {
    15                 String writeStr = "张柳宁 - " + UUID.randomUUID().toString() + System.lineSeparator();//待写入的数据
    16                 charBuffer.put(writeStr);
    17                 charBuffer.flip();
    18                 encoder.encode(charBuffer, byteBuffer, true);
    19                 byteBuffer.flip();
    20                 fileChannel.write(byteBuffer);
    21                 byteBuffer.clear();
    22                 charBuffer.clear();
    23             }
    24 
    25         }
    26     }
    • 缓冲区 

    缓冲区根据缓存的内容不同,可以分为如下几类

    ByteBuffer
    CharBuffer
    DoubleBuffer
    FloatBuffer
    IntBuffer
    LongBuffer 
    ShortBuffer 
    MappedBytesBuffer  - 用于映射内存与文件
     

    Buffer本质是就是一块内存区,可以用来读写数据,这块内存被NIO包装起来,对外提供了一系列便于开发的接口

    先来说说Buffer的共性

    1、capacity,所有的缓冲区都有一个初始大小,它代表了这块缓冲区的容量,不可改变

    2、limit,读写操作允许的最大位置。刚开始创建的时候,limit等于capacity

    3、position,当前读写位置。初始化时为0.一旦写数据,写一个就往后移动一个单元,最大值为capacity-1

    Buffer提供的很多方式都是在对以上三个属性进行操作

    举例:

      读缓冲区前:调用flip,limit设为当前的position,position设为0.这样在读取的时候才能将有效数据全部读取出来

      读缓冲区后:如果全部读完了,调用clear,对Buffer重新初始化,compact 方法会将已经读取到的数据清除出缓冲区,未读取的数据通通往前移

      重复读取:rewind,将position设为0,limit保持不变

    其他操作

      mark,记录当前position

      reset,恢复到mark时候的position

    其他方法详见API

    关于limit,Buffer在读和写的时候含义有所区别,见下图

    • 单通道,多缓冲区读写

    1、将一个通道中的数据一次性写入到多个缓冲区中

    ByteBuffer header = ByteBuffer.allocate(128);
    ByteBuffer body   = ByteBuffer.allocate(1024);
    ByteBuffer[] bufferArray = { header, body };
    channel.read(bufferArray);

    在写入缓冲区数组过程中,按照顺序去填充缓冲区,适合报文长度固定的情况。

    2、多个缓冲区的数据写入到一个通道中

    ByteBuffer header = ByteBuffer.allocate(128);
    ByteBuffer body   = ByteBuffer.allocate(1024);
    //write data into buffers
    ByteBuffer[] bufferArray = { header, body };
    channel.write(bufferArray);

    按照顺序,从position到limit之间的数据会被写入到channel中

    • 文件传输
        public void copyFile() {
            try (FileChannel srcChannel = FileChannel.open(Paths.get("/Users/sherry/WorkPath/Git/LinkTest/zln-learning/testDir/content.html"), StandardOpenOption.READ);
                 FileChannel destChannel = FileChannel.open(Paths.get("/Users/sherry/WorkPath/Git/LinkTest/zln-learning/testDir/content_bak.html"), StandardOpenOption.CREATE, StandardOpenOption.WRITE)) {
    //            srcChannel.transferTo(0, srcChannel.size(), destChannel);//等效
                destChannel.transferFrom(srcChannel, 0, srcChannel.size());//等效
            } catch (IOException e) {
                logger.error(e.getMessage(), e);
            }
        }
  • 相关阅读:
    HTML(图像img、表格table、列表)
    HTML(标题h、段落p、文本格式化、链接a、头部head)
    List的复制 (浅拷贝与深拷贝)
    最新CentOS6.5安装Docker, 使用阿里云源下载(亲测)
    VirtualBox安装CentOS6.5
    P1010 幂次方 题解
    P1469 找筷子 题解
    P1866 编号 题解
    EasyNVR通道离线但视频流可正常播放是什么原因导致的?
    EasyNVR通过国标GB28181协议级联出现报错及播放不了的问题调整
  • 原文地址:https://www.cnblogs.com/sherrykid/p/6009378.html
Copyright © 2011-2022 走看看