[Netty学习笔记]三、NIO核心组件Channel

通道(Channel)

NIO的通道类似于流,但有些区别如下:

  • 通道可以同时进行读写,而流只能读或者只能写
  • 通道可以实现异步读写数据
  • 通道可以从缓冲区读数据,也可以写数据到缓冲区

Channel在NIO中是一个接口,常用的Channel类有:FileChannelDatagramChannelServerSocketChannelSocketChannel(ServerSocketChannel类似ServerSocket、SocketChannel类似Socket)

FileChannel用于文件的数据读写,DatagramChannel用于UDP的数据读写,ServerSocketChannelSocketChannel用于TCP的数据读写。

FileChannel类

FileChannel主要用来对本地文件进行IO操作的,常用方法如下:

  • public int read(ByteBuffer dst) ,从通道读取数据并放到缓冲区中
  • public int write(ByteBuffer src) ,把缓冲区的数据写到通道中
  • public long transferFrom(ReadableByteChannel src, long position, long count),从目标通道中复制数据到当前通道
  • public long transferTo(long position, long count, WritableByteChannel target),把数据从当前通道复制给目标通道
FileChannel例子

Demo1:将随机字符串写入到某文件中

public class FileChannelWriteDemo {
    public static void main(String[] args) throws IOException {
        //使用这种方式不需要进行buffer读写转换
//        ByteBuffer byteBuffer=ByteBuffer.wrap("hello".getBytes());

        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
        byteBuffer.put("hello".getBytes());

        //使用流来构造FileChannel
        FileOutputStream fos = new FileOutputStream("1.txt");
        FileChannel channel = fos.getChannel();

        byteBuffer.flip();
        channel.write(byteBuffer);

        channel.close();
        fos.close();

    }
}

Demo2:将上面1.txt中的数据读入程序,并显示出来

public class FileChannelReadDemo {

    public static void main(String[] args) throws Exception {
        //从文件中读取内容 输出出来
        FileInputStream fis = new FileInputStream("1.txt");

        FileChannel channel = fis.getChannel();

        ByteBuffer buffer = ByteBuffer.allocate(fis.available());
        //返回读取的字节数,可能为0(position与limit相等时).如果当前channel已经读取到流的末尾 则返回-1
        int read = channel.read(buffer);
//        buffer.flip();

        //buffer.array()直接获取了buffer内部的数组 不需要读写转换,如果采用原来遍历的方式
        //需要buffer.flip() 否则报错BufferUnderflowException
        System.out.println(new String(buffer.array()).trim());
        channel.close();
        fis.close();
    }

}

Demo3:使用一个ByteBuffer完成文件的读取、写入

/**
 * 2020/1/7 下午3:30
 * 使用buffer和channel 实现文件的拷贝。使用一个channel将文件数据读取到buffer中,
 * 再使用另一个channel从buffer中读取数据
 */
public class FileChannelCopyDemo {
    public static void main(String[] args) throws Exception {


        FileInputStream fis = new FileInputStream("1.txt");
        FileChannel readChannel = fis.getChannel();
        FileOutputStream fos = new FileOutputStream("2.txt");
        FileChannel writeChannel = fos.getChannel();

        ByteBuffer buffer = ByteBuffer.allocate(2);

        while (true) {
            //循环读取、写入数据时 记得clear
            //否则 buffer中内容被读取之后position=limit,下面的read会读不到数据
            //read会变成0 造成在这里死循环
            buffer.clear();
            int read = readChannel.read(buffer);
            if (read == -1) {
                break;
            }
            buffer.flip();
            writeChannel.write(buffer);
        }

        writeChannel.close();
        fos.close();
        readChannel.close();
        fis.close();
    }
}

Demo4:使用FileChannel和transferFrom 完成文件的拷贝

public class FileChannelTransFromDemo {
    public static void main(String[] args) throws Exception {

        FileInputStream fis = new FileInputStream("1.txt");
        FileChannel readChannel = fis.getChannel();
        FileOutputStream fos = new FileOutputStream("3.txt");
        FileChannel writeChannel = fos.getChannel();

        //将文件1.txt拷贝到3.txt中
        long l = writeChannel.transferFrom(readChannel, 0, fis.available());
        System.out.println(l);
        writeChannel.close();
        fos.close();
        readChannel.close();
        fis.close();

    }
}

Demo5:使用 MappedByteBuffer, 可以让文件直接在内存(堆外的内存)中进行修改

/**
 * 2020/1/7 下午4:27
 * MappedByteBuffer可以实现数据在堆外内存中直接修改 而不需要拷贝一次
 */
public class MappedByteBufferDemo {

    public static void main(String[] args) throws Exception{
        //使用MapMode.READ_WRITE 必须保证读取的文件是可读写的,否则抛出异常
//        FileInputStream fis=new FileInputStream("1.txt");
//        FileChannel channel = fis.getChannel();

        RandomAccessFile accessFile=new RandomAccessFile("1.txt","rw");
        FileChannel channel = accessFile.getChannel();
        /**
         * FileChannel.MapMode.READ_WRITE 使用的读写模式
         * 0 : 可以直接修改的起始位置
         * 5: 是映射到内存的大小(不是索引位置) ,即将 1.txt 的多少个字节映射到内存
         * 可以直接修改的范围就是 0-5
         */
        MappedByteBuffer mappedByteBuffer = channel.map(FileChannel.MapMode.READ_WRITE, 0, 5);

        mappedByteBuffer.put(0,(byte)'A');

        channel.close();
        accessFile.close();


    }

}

Demo6:使用多个Buffer(buffer数组)来完成读写操作

/**
 * 2020/1/7 下午8:03
 * 从buffer中读取数据,可以是buffer数组,会依次读取buffer数组中的元素
 */
public class GatheringDemo {
    public static void main(String[] args) throws Exception {

        FileOutputStream fos = new FileOutputStream("4.txt");

        FileChannel channel = fos.getChannel();

        ByteBuffer[] buffers = new ByteBuffer[2];
        buffers[0] = ByteBuffer.allocate(4);
        buffers[1] = ByteBuffer.allocate(8);

        for (int i = 0; i < 4; i++) {
            buffers[0].put((byte) (65 + i));
        }
        for (int i = 0; i < 8; i++) {
            buffers[1].put((byte) (65 + i));
        }

        Stream.of(buffers).forEach(buffer->buffer.flip());

        channel.write(buffers);
        channel.close();
        fos.close();

    }
}
/**
 * 2020/1/7 下午7:46
 * 将数据写入buffer时,可以指定buffer数组,数据会自动依次写入到buffer数组的每个元素里
 */
public class ScatteringDemo {

    public static void main(String[] args) throws Exception {
        FileInputStream fis = new FileInputStream("1.txt");

        ByteBuffer[] buffers = new ByteBuffer[2];

        buffers[0]=ByteBuffer.allocate(4);
        buffers[1]=ByteBuffer.allocate(32);

        FileChannel channel = fis.getChannel();

        channel.read(buffers);


        Stream.of(buffers)
                .forEach(buffer -> buffer.flip());

        Stream.of(buffers).forEach(buffer -> {
            while (buffer.hasRemaining()) {
                System.out.println((char)buffer.get());
            }
        });


    }

}
发布了116 篇原创文章 · 获赞 23 · 访问量 8万+

猜你喜欢

转载自blog.csdn.net/zyxwvuuvwxyz/article/details/104112684