• 4.NIO_Channel 通道


    1.通道(Channel)

      由 java.nio.channels 包定义的。Channel 表示 IO 源与目标打开的连接。Channel 类似于传统的“流”。只不过 Channel

    本身不能直接访问数据,Channel 只能与Buffer 进行交互

      Java 为 java.nio.channels.Channel 接口提供的最主要实现类如下:

      1. FileChannel:用于读取、写入、映射和操作文件的通

      2. DatagramChannel:通过 UDP 读写网络中的数据通道

      3. SocketChannel:通过 TCP 读写网络中的数据

      4. ServerSocketChannel:可以监听新进来的 TCP 连接,对每一个新进来的连接都会创建一个 SocketChannel

    1.获取通道

       1.Java 针对支持通道的类提供了 getChannel() 方法

      

      2.在 JDK 1.7 中的 NIO.2 针对各个通道提供了静态方法 open(),打开并返回指定通道

      3.获取通道的其他方式是使用 Files 类的静态方法 newByteChannel() 获取字节通道

    2.通道的数据读取:

      1. 从 Channel 读取数据到 Buffer:int bytesRead = inChannel.read(buf);

      2. 将 Buffer 中数据写入 Channel:int bytesWritten = inChannel.write(buf);

    3.通道之间的数据传输:

      transferFrom():将数据从源通道传输到其他 Channel 中:
           transferTo():将数据从源通道传输到其他 Channel 中

    4.分散(Scatter)和聚集(Gather):

      分散读取(Scattering Reads)是指从 Channel 中读取的数据“分散”到多个 Buffer 中

      

      注意:按照缓冲区的顺序,从 Channel 中读取的数据依次将 Buffer 填满

      聚集写入(Gathering Writes)是指将多个 Buffer 中的数据“聚集”到 Channel

      

      注意:按照缓冲区的顺序,写入 position 和 limit 之间的数据到 Channel 

    4.例子代码:

    package cn.nio;
    
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.RandomAccessFile;
    import java.nio.ByteBuffer;
    import java.nio.CharBuffer;
    import java.nio.MappedByteBuffer;
    import java.nio.channels.FileChannel;
    import java.nio.channels.FileChannel.MapMode;
    import java.nio.charset.CharacterCodingException;
    import java.nio.charset.Charset;
    import java.nio.charset.CharsetDecoder;
    import java.nio.charset.CharsetEncoder;
    import java.nio.file.Paths;
    import java.nio.file.StandardOpenOption;
    import java.util.Map;
    import java.util.Map.Entry;
    import java.util.Set;
    
    import org.junit.Test;
    
    /*
     * 一、通道(Channel):用于源节点与目标节点的连接。在 Java NIO 中负责缓冲区中数据的传输。Channel 本身不存储数据,因此需要配合缓冲区进行传输。
     * 
     * 二、通道的主要实现类
     *     java.nio.channels.Channel 接口:
     *         |--FileChannel
     *         |--SocketChannel
     *         |--ServerSocketChannel
     *         |--DatagramChannel
     * 
     * 三、获取通道
     * 1. Java 针对支持通道的类提供了 getChannel() 方法
     *         本地 IO:
     *             FileInputStream/FileOutputStream
     *             RandomAccessFile
     *         网络IO:
     *             Socket
     *             ServerSocket
     *             DatagramSocket
     *         
     * 2. 在 JDK 1.7 中的 NIO.2 针对各个通道提供了静态方法 open()
     * 3. 在 JDK 1.7 中的 NIO.2 的 Files 工具类的 newByteChannel()
     * 
     * 四、通道之间的数据传输
     * transferFrom():将数据从源通道传输到其他 Channel 中:
     * transferTo():将数据从源通道传输到其他 Channel 中
     * 
     * 五、分散(Scatter)与聚集(Gather)
     * 分散读取(Scattering Reads):将通道中的数据分散到多个缓冲区中
     * 聚集写入(Gathering Writes):将多个缓冲区中的数据聚集到通道中
     * 
     * 六、字符集:Charset
     * 编码:字符串 -> 字节数组
     * 解码:字节数组  -> 字符串
     * 
     */
    public class TestChannel {
        
        //字符集
        @Test
        public void test6() throws IOException{
            //指定编码方式
            Charset cs1 = Charset.forName("GBK");
            //获取编码器
            CharsetEncoder ce = cs1.newEncoder();
            //获取解码器
            CharsetDecoder cd = cs1.newDecoder();
            CharBuffer cBuf = CharBuffer.allocate(1024);
            cBuf.put("威武!");
            cBuf.flip();
            //编码
            ByteBuffer bBuf = ce.encode(cBuf);
            for (int i = 0; i < 12; i++) {
                System.out.println(bBuf.get());
            }
            //解码
            bBuf.flip();
            CharBuffer cBuf2 = cd.decode(bBuf);
            System.out.println(cBuf2.toString());
            System.out.println("------------------------------------------------------");
            
            Charset cs2 = Charset.forName("GBK");
            bBuf.flip();
            CharBuffer cBuf3 = cs2.decode(bBuf);
            System.out.println(cBuf3.toString());
        }
    
        //遍历所有的字符集
        @Test
        public void test5(){
            Map<String, Charset> map = Charset.availableCharsets();
            
            Set<Entry<String, Charset>> set = map.entrySet();
            
            for (Entry<String, Charset> entry : set) {
                System.out.println(entry.getKey() + "=" + entry.getValue());
            }
        }
        
        //分散和聚集
        @Test
        public void test4() throws IOException{
            RandomAccessFile raf1 = new RandomAccessFile("C:/sss.txt", "rw");
            //1. 获取通道
            FileChannel channel1 = raf1.getChannel();
            //2. 分配指定大小的缓冲区
            ByteBuffer buf1 = ByteBuffer.allocate(100);
            ByteBuffer buf2 = ByteBuffer.allocate(1024);
            
            //3. 分散读取
            ByteBuffer[] bufs = {buf1, buf2};
            //将通道中的数据分散到多个缓冲区中:从 Channel 读取数据到 Buffer
            channel1.read(bufs);
            
            for (ByteBuffer byteBuffer : bufs) {
                byteBuffer.flip();
            }
            
            System.out.println(new String(bufs[0].array(), 0, bufs[0].limit()));
            System.out.println("-----------------");
            System.out.println(new String(bufs[1].array(), 0, bufs[1].limit()));
            
            //4. 聚集写入
            RandomAccessFile raf2 = new RandomAccessFile("C:/2.txt", "rw");
            FileChannel channel2 = raf2.getChannel();
            //将 Buffer 中数据写入 Channel
            channel2.write(bufs);
        }
        
        //通道之间的数据传输(直接缓冲区)
        @Test
        public void test3() throws IOException{
            FileChannel inChannel = FileChannel.open(Paths.get("d:/1.mkv"), StandardOpenOption.READ);
            FileChannel outChannel = FileChannel.open(Paths.get("d:/2.mkv"), StandardOpenOption.WRITE, StandardOpenOption.READ, StandardOpenOption.CREATE);
    
            //inChannel.transferTo(0, inChannel.size(), outChannel);
            outChannel.transferFrom(inChannel, 0, inChannel.size());
            
            inChannel.close();
            outChannel.close();
        }
        
        //使用直接缓冲区完成文件的复制(内存映射文件)(支持byteBuffer)
        @Test
        public void test2() throws IOException{//2127-1902-1777
            long start = System.currentTimeMillis();
            //Paths工具类,
            FileChannel inChannel = FileChannel.open(Paths.get("C:/sss.txt"), StandardOpenOption.READ);
            //完成文件的复制,StandardOpenOption.CREATE文件存在与否,都创建
            FileChannel outChannel = FileChannel.open(Paths.get("C:/xxx.txt"), StandardOpenOption.WRITE, StandardOpenOption.READ, StandardOpenOption.CREATE);
            
            //创建内存映射文件:直接字节缓冲区还可以通过 FileChannel 的 map() 方法 将文件区域直接映射到内存中来创建,不需要通道,直接操作缓冲区
            MappedByteBuffer inMappedBuf = inChannel.map(MapMode.READ_ONLY, 0, inChannel.size());
            MappedByteBuffer outMappedBuf = outChannel.map(MapMode.READ_WRITE, 0, inChannel.size());
            
            //直接对缓冲区进行数据的读写操作
            byte[] dst = new byte[inMappedBuf.limit()];
            //批量读取多个字节到 dst 中
            inMappedBuf.get(dst);
            //利用 put() 存入数据到缓冲区中
            outMappedBuf.put(dst);
            
            inChannel.close();
            outChannel.close();
            
            long end = System.currentTimeMillis();
            System.out.println("耗费时间为:" + (end - start));
        }
        
        //利用通道完成文件的复制(非直接缓冲区)
        @Test
        public void test1(){//10874-10953
            long start = System.currentTimeMillis();
            FileInputStream fis = null;
            FileOutputStream fos = null;
            //①获取通道
            FileChannel inChannel = null;
            FileChannel outChannel = null;
            try {
                fis = new FileInputStream("C:/sss.txt");
                fos = new FileOutputStream("C:/ddd.txt");
                
                inChannel = fis.getChannel();
                outChannel = fos.getChannel();
                
                //②分配指定大小的缓冲区
                ByteBuffer buf = ByteBuffer.allocate(1024);
                
                //③将通道中的数据存入缓冲区中
                while(inChannel.read(buf) != -1){
                    //切换读取数据的模式
                    buf.flip();
                    //④将缓冲区中的数据写入通道中
                    outChannel.write(buf);
                    buf.clear(); //清空缓冲区
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if(outChannel != null){
                    try {
                        outChannel.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                
                if(inChannel != null){
                    try {
                        inChannel.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                
                if(fos != null){
                    try {
                        fos.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                
                if(fis != null){
                    try {
                        fis.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
            long end = System.currentTimeMillis();
            System.out.println("耗费时间为:" + (end - start));
        }
    }

      

      

      

  • 相关阅读:
    next_permutation
    P1087 FBI树
    P4047 [JSOI2010]部落划分
    买礼物
    P2121 拆地毯
    Nebula Graph 在大规模数据量级下的实践和定制化开发
    深入了解kafka系列-消费者
    一分钟教你搭建WebRTC流媒体服务器Janus-gateway
    什么是"前端工程化"?
    斗鱼Juno 监控中心的设计与实现
  • 原文地址:https://www.cnblogs.com/yaboya/p/9172394.html
Copyright © 2020-2023  润新知