NIO assembly Channel

basic introduction

  1. NIO channel flow is similar, but some differences:
    • Channels can be simultaneously read and write, and the flow can either read or written
    • Channel may be implemented asynchronously read and write data
    • Channel can buffer (Buffer) to read data from, you can also write data to the buffer
  2. BIO the stream is unidirectional, e.g. FileInputStream objects can only be operated to read data, and in NIO channel (Channel) is bi-directional, it can be read operation, write operation.
  3. Channel is an interface in NIO: public interface Channel extends Closeable {}
  4. Commonly used Channel category are: FileChannel, DatagramChannel, ServerSocketChannel and SocketChannel.
  5. FileChannel for data read and write files, DatagramChannel of UDP for data read and write, ServerSocketChannel SocketChannel for data read and write and the TCP.

FileChannel类

  • FileChannel mainly used for local file IO operations, common methods are:

    • public int read (ByteBuffer dst): read data from and into the buffer channel
    • public int write (ByteBuffer src): write the buffer data channel
    • public long transferFrom (ReadableByteChannel src, long position, long count), copy data from the destination channel to the current channel
    • public long transferTo (long position, long count, WritableByteChannel target), copying the data from the current channel to the target channel
  • A simple demo

    • schematic diagram

      1575686161129

      • Note that the need to flip ByteBuffer be converted in the changed mode data into ByteBuffer writes the data into the Channel.
    • Code written in Case

      package com.ronnie.nio;
      
      
      import java.io.FileOutputStream;
      import java.io.IOException;
      import java.nio.ByteBuffer;
      import java.nio.channels.FileChannel;
      
      public class NIOFileChannel01 {
          public static void main(String[] args) throws IOException {
              String str = "hello, the world of code";
      
              // 创建一个输出流 -> channel
              FileOutputStream fileOutputStream = new FileOutputStream("E:/fun/nio/doc/text.txt");
      
              // 通过 fileOutputStream获取对应的 FileChannel
              // 需要注意的是: 此fileChannel真实类型是 FileChannelImpl
              FileChannel fileChannel = fileOutputStream.getChannel();
      
              // 创建缓冲区 ByteBuffer
              ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
      
              // 将 str 放入 byteBuffer
              byteBuffer.put(str.getBytes());
      
              // 对byteBuffer进行反转(flip)
              byteBuffer.flip();
      
              // 将byteBuffer 数据写入到channel
              fileChannel.write(byteBuffer);
              fileOutputStream.close();
          }
      }
    • detail

      • fileOutputStream.getChannel () fileChannel real type of acquisition is FileChannelImpl, is FileChannel implementation class, FileChannel itself is only an abstract class.

        1575686459262

      • the getChannel () Method:

            /**
             * Returns the unique {@link java.nio.channels.FileChannel \
             * FileChannel} object associated with this file output stream.
             * 返回与此文件输出流相关的唯一FileChannel对象
             *
             * <p> The initial {@link java.nio.channels.FileChannel#position()
             * position} of the returned channel will be equal to the
             * number of bytes written to the file so far unless this stream is  
             * in append mode, in which case it will be equal to the size of the 
             * file.
             * 返回的channel的初始位置与写入到此文件此文件的byte数一致(除非这个流式可
             * 添加模式, 该模式下channel的初始位置会与该文件的大小一致)
             * Writing bytes to this stream will increment the channel's position
             * accordingly.  
             * 将字节码写入到该流会增加该channel的位置
             * Changing the channel's position, either explicitly or by
             * writing, will change this stream's file position.
             * 改变管道的位置, 无论是显示的修改还是通过写入修改, 都会改变此流的文件位置
             *
             * @return  the file channel associated with this file output stream
             *
             * @since 1.4
             * @spec JSR-51
             */
            public FileChannel getChannel() {
                // 同步锁锁了当前输出流对象
                synchronized (this) {
                    // 如果管道为空
                    if (channel == null) {
                        // 就创建一个新的FileChannelImpl对象赋给channel
                        channel = FileChannelImpl.open(fd, path, false, true, append, this);
                    }
                    return channel;
                }
            }
        • FileChannelImpl.open () method:

              public static FileChannel open(FileDescriptor var0, String var1, boolean var2, boolean var3, boolean var4, Object var5) {
                  return new FileChannelImpl(var0, var1, var2, var3, var4, var5);
              }
    • Read Code Case

      package com.ronnie.nio;
      
      import java.io.File;
      import java.io.FileInputStream;
      import java.io.IOException;
      import java.nio.ByteBuffer;
      import java.nio.channels.FileChannel;
      
      public class NIOFileChannel02 {
          public static void main(String[] args) throws IOException {
      
              // 创建文件输入流
              File file = new File("E:/fun/nio/doc/text.txt");
      
              FileInputStream fileInputStream = new FileInputStream(file);
      
              // 通过fileInputStream 获取对应的fileChannel -> 实际类型 FileChannelImpl
              FileChannel fileChannel = fileInputStream.getChannel();
      
              // 创建缓冲区
              ByteBuffer byteBuffer = ByteBuffer.allocate((int) file.length());
      
              // 将通道的数据读入到buffer中
              fileChannel.read(byteBuffer);
      
              // 将字节数据转成String
              System.out.println(new String(byteBuffer.array()));
              fileInputStream.close();
          }
      }
      
      • byteBuffer.array () returns an array of bytes that the bottom, and then convert it to a String.
    • Buffer read a complete case file

      • schematic diagram

        1575689046570

      • Code

        package com.ronnie.nio;
        
        import java.io.FileInputStream;
        import java.io.FileOutputStream;
        import java.io.IOException;
        import java.nio.ByteBuffer;
        import java.nio.channels.FileChannel;
        
        public class NIOFileChannel03 {
            public static void main(String[] args) throws IOException {
                FileInputStream fileInputStream = new FileInputStream("1.txt");
                FileOutputStream fileOutputStream = new FileOutputStream("2.txt");
        
                FileChannel channel01 = fileInputStream.getChannel();
                FileChannel channel02 = fileOutputStream.getChannel();
        
                ByteBuffer byteBuffer = ByteBuffer.allocate(512);
        
                // 循环读取
                while (true){
                    // 非常重要的操作, 复位(重置标志位)
                    // 如果没写, 当position与limit相等时, read永远为0, 进入死循环
                    byteBuffer.clear();
        
                    int read = channel01.read(byteBuffer);
                    if (read == -1){
                        break;
                    }
                    // 将buffer 中的数据写入到 channel02
                    byteBuffer.flip();
                    channel02.write(byteBuffer);
                }
        
                // 关闭流
                fileInputStream.close();
                fileOutputStream.close();
            }
        }
        
    • Use transferFrom () method to copy files case

      package com.ronnie.nio;
      
      import java.io.FileInputStream;
      import java.io.FileOutputStream;
      import java.io.IOException;
      import java.nio.channels.FileChannel;
      
      public class NIOFileChannel04 {
          public static void main(String[] args) throws IOException {
      
              // 创建线管的流
              FileInputStream fileInputStream = new FileInputStream("E:/fun/nio/doc/hadoop.jpg");
              FileOutputStream fileOutputStream = new FileOutputStream("E:/fun/nio/doc/hadoop2.jpg");
      
              // 获取各个流对应的fileChannel
              FileChannel sourceChannel = fileInputStream.getChannel();
              FileChannel destinationChannel = fileOutputStream.getChannel();
      
              // 使用transferFrom完成拷贝
              destinationChannel.transferFrom(sourceChannel, 0, sourceChannel.size());
      
              // 关闭相关通道和流
              sourceChannel.close();
              destinationChannel.close();
              fileInputStream.close();
              fileOutputStream.close();
      
          }
      }

Notes and details of Buffer and Channel

  • ByteBuffer support type of put and get, put into what type of data, get should be used to remove the corresponding data type, or it may have abnormal BufferUnderflowException

    package com.ronnie.nio;
    
    import java.nio.ByteBuffer;
    
    public class NIOByteBufferPutGet {
        public static void main(String[] args) {
    
            // 创建一个Buffer
            ByteBuffer buffer = ByteBuffer.allocate(64);
    
            // 类型化放入数据
            buffer.putInt(100);
            buffer.putLong(9);
            buffer.putChar('w');
            buffer.putShort((short) 4);
    
            // 取出
            buffer.flip();
    
            System.out.println();
    
            System.out.println(buffer.getInt());
            System.out.println(buffer.getLong());
            System.out.println(buffer.getChar());
            System.out.println(buffer.getShort());
            // 加个没的, 让它报错
            System.out.println(buffer.getLong());
        }
    }
    100
    9
    w
    4
    Exception in thread "main" java.nio.BufferUnderflowException
      at java.nio.Buffer.nextGetIndex(Buffer.java:506)
      at java.nio.HeapByteBuffer.getLong(HeapByteBuffer.java:412)
      at com.ronnie.nio.NIOByteBufferPutGet.main(NIOByteBufferPutGet.java:26)
  • A Common Buffer may be converted to read-only Buffer, can not be written to the converted

    package com.ronnie.nio;
    
    import java.nio.ByteBuffer;
    
    public class ReadOnlyBuffer {
        public static void main(String[] args) {
    
            // 创建一个buffer
            ByteBuffer buffer = ByteBuffer.allocate(64);
    
            for (int i = 0; i < 64; i++){
                buffer.put((byte) i);
            }
    
            // 读取
            buffer.flip();
    
            // 得到一个只读的Buffer
            ByteBuffer readOnlyBuffer = buffer.asReadOnlyBuffer();
            System.out.println(readOnlyBuffer.getClass());
    
            // 读取
            while (readOnlyBuffer.hasRemaining()){
                System.out.println(readOnlyBuffer.get());
            }
    
            // 会抛出ReadOnlyBufferException
            readOnlyBuffer.put((byte) 100);
        }
    }
  • NIO also provides MappedByteBuffer, allowing files to be modified directly in memory (memory outside of the heap) in.

    package com.ronnie.nio;
    
    import java.io.IOException;
    import java.io.RandomAccessFile;
    import java.nio.MappedByteBuffer;
    import java.nio.channels.FileChannel;
    
    /**
     *  MappedByteBuffer 可让文件直接在堆外内存修改, 操作系统无需将数据拷贝到用户态内存中,
     *  即零拷贝, kafka底层就是依靠netty实现了零拷贝
     */
    
    public class MappedByteBufferTest {
        public static void main(String[] args) throws IOException {
            RandomAccessFile randomAccessFile = new RandomAccessFile("1.txt", "rw");
    
            // 获取对应的通道
            FileChannel channel = randomAccessFile.getChannel();
    
            /**
             *  参数1: FileChannel.MapMode.READ_WRITE 使用的读写模式
             *  参数2: 0: 可以直接修改的起始位置
             *  参数3: 5: 是映射到内存的大小(不是索引位置), 即将 1.txt 的多少个字节映射到
             *  内存
             *  可以直接修改的范围就是0~5, 不到5
             */
            MappedByteBuffer mappedByteBuffer = channel.map(FileChannel.MapMode.READ_WRITE, 0, 5);
    
            mappedByteBuffer.put(0, (byte) 'S');
            mappedByteBuffer.put(3, (byte) '8');
            // mappedByteBuffer.put(5, (byte) 'G'); 会抛出数组越界异常
    
            randomAccessFile.close();
            System.out.println("修改成功");
        }
    }
    
  • Before I said read and write operations are accomplished by a Buffer, the NIO supports read and write operations performed by a plurality Buffer (Buffer i.e. array), i.e. Scattering (dispersion) and Gathering (polymerization)

    • Case Code:

      package com.ronnie.nio;
      
      
      import java.io.IOException;
      import java.net.InetSocketAddress;
      import java.nio.Buffer;
      import java.nio.ByteBuffer;
      import java.nio.channels.ServerSocketChannel;
      import java.nio.channels.SocketChannel;
      import java.util.Arrays;
      
      /**
       *  Scattering: 将数据写入到buffer时, 可以采用buffer数组, 依次写入 [分散]
       *  Gathering: 从buffer读取数据时, 可以采用buffer数组, 依次读
       */
      public class ScatteringAndGatheringTest {
          public static void main(String[] args) throws IOException {
      
              // 使用 ServerSocketChannel 和 SocketChannel 网络
      
              ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
              InetSocketAddress inetSocketAddress = new InetSocketAddress(7000);
      
              // 绑定端口到socket, 并启动
              serverSocketChannel.socket().bind(inetSocketAddress);
      
              // 创建Buffer数组
              ByteBuffer[] byteBuffers = new ByteBuffer[2];
      
              byteBuffers[0] = ByteBuffer.allocate(5);
              byteBuffers[1] = ByteBuffer.allocate(3);
      
              // 等待客户端连接(telnet)
              SocketChannel socketChannel = serverSocketChannel.accept();
              // 假定从客户端接收8个字节
              int messageLength = 8;
      
              // 循环的读取
              while (true){
                  int byteRead = 0;
                  while (byteRead < messageLength){
                      long l = socketChannel.read(byteBuffers);
                      // 累计读取的字节数
                      byteRead += 1;
                      System.out.println("byteRead = " + byteRead);
                      // 使用流打印, 查看当前buffer的position和limit
                      Arrays.stream(byteBuffers).map(buffer -> "position = " + buffer.position() + ", limit = " + buffer.limit()).forEach(System.out::println);
      
                  }
                  // 将所有的buffer进行反转(flip)
                  Arrays.asList(byteBuffers).forEach(Buffer::flip);
      
                  // 将数据读出显示到客户端
                  long byteWrite = 0;
                  while(byteWrite < messageLength){
                      long l = socketChannel.write(byteBuffers);// 回写
                      byteWrite += 1;
                  }
                  // 将所有的 buffer 进行 clear操作
                  Arrays.asList(byteBuffers).forEach(Buffer::clear);
      
                  System.out.println("byteRead := " + byteRead + " byteWrite = " + byteWrite + ", messageLength = " + messageLength);
              }
      
          }
      }
      
    • windows open win + R, then enter cmd, telnet 127.0.0.1 7000, you can send data, please do not open: Control Panel -> Programs -> Enable or disable Windows features -> open Telnet Client

Guess you like

Origin www.cnblogs.com/ronnieyuan/p/12001383.html