• 26 Java学习之NIO Channel(一)(待补充)


     一. Channel

    在标准的IO当中,都是基于字节流/字符流进行操作的,而在NIO中则是是基于Channel和Buffer进行操作,其中的Channel的虽然模拟了流的概念,实则大不相同。

    1.1 概念

    Channel(通道)用于在字节缓冲区和位于通道另一侧的实体(通常是一个文件或套接字)之间有效的传输数据。有点抽象,不过我们可以根据它的用途来理解:

    通道主要用于传输数据,从缓冲区的一侧传到另一侧的实体(如文件、套接字...),反之亦然;

    通道是访问IO服务的导管,通过通道,我们可以以最小的开销来访问操作系统的I/O服务;

    顺便说下,缓冲区是通道内部发送数据和接收数据的端点,

    基本上,所有的 IO 在NIO 中都从一个Channel 开始。Channel 有点像流。 数据可以从Channel读到Buffer中,也可以从Buffer 写到Channel中。这里有个图示: 
                                                          

    下面是JAVA NIO中的一些主要Channel的实现: java.nio.channels包中的Channel接口的已知实现类

    • FileChannel:从文件中读写数据。
    • DatagramChannel:能通过UDP读写网络中的数据。
    • SocketChannel:能通过TCP读写网络中的数据。
    • ServerSocketChannel:可以监听新进来的TCP连接,像Web服务器那样。对每一个新进来的连接都会创建一个SocketChannel。

    正如你所看到的,这些通道涵盖了UDP 和 TCP 网络IO,以及文件IO。

    该接口如下所示:另外,关于通道Channel接口的定义,很简单,只有两个方法,判断通道是否打开和关闭通道;

    1 public interface Channel extends Closeable {
    2 
    3     public boolean isOpen();
    4 
    5     public void close() throws IOException;
    6 
    7 }
    View Code

    与缓冲区不同,通道API主要由接口指定。不同的操作系统上通道实现(Channel Implementation)会有根本性的差异,所以通道API仅仅描述了可以做什么。因此很自然地,通道实现经常使用操作系统的本地代码。通道接口允许您以一种受控且可移植的方式来访问底层的I/O服务。

    1.2  创建通道

    通道主要分为两大类,文件(File)通道和套接字(socket)通道;下面分别看看这几个通道是如何创建的:

    (1)创建FileChannel通道

    FileChannel通道只能通过在一个打开的RandomAccessFile、FileInputStream或FileOutputStream对象上调用getChannel()方法来获取,比如:

    RandomAccessFile accessFile=new RandomAccessFile("C:\Users\hermioner\Desktop\test.txt", "rw");
    FileChannel fileChannel=accessFile.getChannel();

    (2)创建SocketChannel通道

    SocketChannel socketChannel=SocketChannel.open();
    socketChannel.connect(new InetSocketAddress("somehost", someport));

    (3)创建ServerSocketChannel通道

     ServerSocketChannel ssc = ServerSocketChannel.open();
     ssc.socket().bind(new InetSocketAddress(somelocalport));

    (4)创建DatagramChannel通道

    DatagramChannel dc = DatagramChannel.open( );

    1.3 单向或者双向通道

    (1)读

    public interface ReadableByteChannel extends Channel {
        public int read(ByteBuffer dst) throws IOException;
    }

    (2)写

    public interface WritableByteChannel
        extends Channel
    {
        public int write(ByteBuffer src) throws IOException;
    
    }

    (3)读写

    public interface ByteChannel
        extends ReadableByteChannel, WritableByteChannel
    {
    
    }     

            通道既可以是单向的也可以是双向的。只实现ReadableByteChannel接口中的read()方法或者只实现WriteableByteChannel接口中的write()方法的通道皆为单向通道,同时实现ReadableByteChannel和WriteableByteChannel为双向通道,比如ByteChannel。对于socket通道来说,它们一直是双向的,而对于FileChannel来说,它同样实现了ByteChannel,但是我们知道通过FileInputStream的getChannel()获取的FileChannel只具有文件的只读权限,那此时的在该通道调用write()会出现什么情况?不出意外的抛出了NonWriteChannelException异常。 
    通过以上,我们得出结论:通道都与特定的I/O服务挂钩,并且通道的性能受限于所连接的I/O服务的性质。

    1.4 Scatter(分散)和Gather(聚集)

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

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

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

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

    1.5 transferFrom & transferTo

    代表了将数据从源通道传输到其它Channel中

    1.6 通道的工作模式

    通道的工作模式有两种:阻塞或非阻塞。在非阻塞模式下,调用的线程不会休眠,请求的操作会立刻返回结果;在阻塞模式下,调用的线程会产生休眠。另外除FileChannel不能运行在非阻塞模式下,其余的通道都可阻塞运行也可以以非阻塞的方式运行。

    二. FileChannel

    FileChannel和RandomAccessFile的比较:

    2.1 使用文件通道读数据

     1 package com.test.a;
     2 
     3 import java.io.File;
     4 import java.io.FileInputStream;
     5 import java.io.IOException;
     6 import java.nio.ByteBuffer;
     7 import java.nio.channels.FileChannel;
     8 
     9 public class Test {
    10     public static void main(String[] args) throws IOException, IOException, ClassNotFoundException {
    11         File file = new File("C:\Users\hermioner\Desktop\test.txt");
    12         FileInputStream fis = new FileInputStream(file);
    13         FileChannel fc = fis.getChannel();
    14         ByteBuffer bb = ByteBuffer.allocate(35);
    15         fc.read(bb);
    16         bb.flip();
    17         while (bb.hasRemaining()) {
    18             System.out.print((char) bb.get());
    19         }
    20         bb.clear();
    21         fc.close();
    22 
    23     }
    24 }
    25 I love china
    26 I love my family
    View Code

    test.txt种的内容为:

    说明:使用read方法将内容读取到缓冲区内即可(从通道种读取数据),缓冲区内有了数据,就可以使用前文对于缓冲区的操作读取数据了

    2.2 使用文件通道写数据

     1 package com.test.a;
     2 
     3 import java.io.File;
     4 import java.io.IOException;
     5 import java.io.RandomAccessFile;
     6 import java.nio.ByteBuffer;
     7 import java.nio.channels.FileChannel;
     8 
     9 public class Test {
    10     public static void main(String[] args) throws IOException, IOException, ClassNotFoundException {
    11         File file = new File("C:\Users\hermioner\Desktop\test.txt");
    12         RandomAccessFile raf = new RandomAccessFile(file, "rw");
    13         FileChannel fc = raf.getChannel();
    14         ByteBuffer bb = ByteBuffer.allocate(10);
    15         String str = "hello";
    16         bb.put(str.getBytes());//最多写10个一次,否则异常
    17         bb.flip();
    18         fc.write(bb);
    19         bb.clear();
    20         fc.close();
    21     }
    22 }
    View Code

    执行结果:hello写入了test.txt文件

    说明:write方法写ByteBuffer中的内容至文件中,注意写之前还是要先把ByteBuffer给flip一下。可能有人觉得这种连续put的方法非常不方便,但是没有办法,之前已经提到过了:通道只能使用ByteBuffer

    2.3 通道的常用方法

    position();返回通道的文件位置

    position(long newPosition):设置通道的文件位置

     1 package com.test.a;
     2 
     3 import java.io.File;
     4 import java.io.IOException;
     5 import java.io.RandomAccessFile;
     6 import java.nio.ByteBuffer;
     7 import java.nio.channels.FileChannel;
     8 
     9 public class Test {
    10     public static void main(String[] args) throws IOException, IOException, ClassNotFoundException {
    11         RandomAccessFile raf = new RandomAccessFile("C:\Users\hermioner\Desktop\test.txt","rw");
    12         FileChannel fis = raf.getChannel();
    13         System.out.println("此通道文件的总长度:" +fis.size());
    14         //当前通道的文件位置
    15         long position = fis.position();
    16         System.out.println("通道当前的位置:" + position);
    17         //设置新的通道文件位置,从这个位置开始读取
    18         fis.position(position + 8);
    19         long position2 = fis.position();
    20         System.out.println("通道当前的位置:" + position2);
    21         ByteBuffer buffer = ByteBuffer.allocate(50);
    22         fis.read(buffer);
    23         buffer.flip();
    24         while(buffer.hasRemaining())
    25         {
    26             System.out.print((char)buffer.get());
    27         }
    28         buffer.clear();
    29         fis.close();
    30     }
    31 }
    32 
    33 此通道文件的总长度:13
    34 通道当前的位置:0
    35 通道当前的位置:8
    36 world
    View Code

    假设test.txt文件中的内容为:

    2.4 FileChannel的一般使用规则

    (1)打开FileChannel

         无法直接打开FileChannel(FileChannel是抽象类),需要通过 InputStream , OutputStream 或 RandomAccessFile 获取FileChannel。

    (2)从FileChannel读取数据/写入数据

    (3)关闭FileChannel

    2.5 size方法

    通过FileChannel实例的size()可获取FileChannel关联文件的大小

    2.6 truncate方法

     三. SocketChannel

    四. ServerSocketChannel

    五. DatagramChannel

     六. 通道工具类

    NIO通道提供了一个便捷的通道类Channels,其中定义了几种静态的工厂方法以便更容易的和流打交道。其中常用的方法如下:

     参考文献:

    https://www.cnblogs.com/xiaoxi/p/6576588.html

    https://www.cnblogs.com/chenpi/p/6481271.html

    https://www.cnblogs.com/szlbm/p/5513155.html

    https://www.cnblogs.com/pony1223/p/8179804.html

    https://www.cnblogs.com/szlbm/p/5513155.html

    https://blog.csdn.net/zcw4237256/article/details/78662762

  • 相关阅读:
    MIPAV
    SPM12manual,统计部分(8-10)笔记
    Django中ORM介绍和字段及字段参数
    Django的路由系统
    django 连接mysql报错
    django启动创建用户失败
    django ORM操作
    Django创建App报错
    Web框架
    Bootstrap框架(组件)
  • 原文地址:https://www.cnblogs.com/Hermioner/p/9784196.html
Copyright © 2020-2023  润新知