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 }
与缓冲区不同,通道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
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 }
执行结果: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
假设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