NIO基础入门(二)之通道Channel
[TOC]
1.概念
nio的第二个创新Channel,既不是扩展也不是增强,而是全新的与I/O服务的直接连接
Channel通常位于字节缓冲区和通道另一边的实体(文件/套接字等)之间有效的数据传输
如
通道两端为缓冲区,是通道发送和接收数据的端点,假设为A/B
A端:一个数据被填充到缓冲区中,接着将缓冲区中的数据冲到通道中–>到达通道B端
B端:flip翻转缓冲区(打开盖子)并清空
与缓冲区不同,通道API主要由接口指定,不同的操作系统上的实现会有根本性差异
所以通道的API仅仅描述可以做什么
(实现经常使用操作系统的本地代码,通道接口允许以一种受控且可移植的方式来访问底层I/O服务)
通道的单/双向
通道可以是单向(unidirectional)或者双向的(bidirectional)
一个channel类实现ReadableByteChannel接口提供的read()方法
另一个channel类实现WritableByteChannel接口提供的write()方法
那么这两个管道都是单向的,
如果一个类同时实现这两个接口,那么他就是双向的,可以双向传输数据
2.Channel源码
1 2 3 4
| public interface Channel extends Closeable { public boolean isOpen(); public void close() throws IOException; }
|
在顶级接口中,只有打开判断和关闭通道两个方法,所有有趣的实现都在他的子接口和实现类中
大部分通道是可中断的,只要有实现InterruptibleChannel可中断接口
Channel接口引申出的其他接口都是面向字节的子接口,包括Writable ByteChannel和ReadableByteChannel等
通道只能在字节缓冲区上操作,层次结构表名其他类型的通道也可以从Channel接口引申,都是字节实现,因为操作系统都是以字节实现底层I/O接口的
3.类型
JDK1.4
文件通道:
FileChannel
套接字通道:
SocketChannel
ServerSocketChannel
DatagramChannel
JDK1.7以后完善AIO增加类型
nio2 增加回调
CompletionHandler
文件通道:
AsynchronousFileChannel 异步文件IO
套接字通道
AsynchronousSocketChannel 客户端
AsynchronousServerSocketChannel 服务器
4.通道操作
FileChannel对象只能通过一个打开的RamdomAccessFile/FileInputStream或者FileOutputStream对象上调用
getChannel()方法来获取,而不能直接创建一个FileChannel对象
建立连接
1 2 3 4 5 6 7 8 9 10 11
| SocketChannel socketChannel = SocketChannel.open(); socketChannel.connect(new InetSocketAddress("localhost",8000));
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); serverSocketChannel.bind(new InetSocketAddress("localhost",8001));
DatagramChannel datagramChannel = DatagramChannel.open(); datagramChannel.connect(new InetSocketAddress("localhost",8002));
FileInputStream inputStream = new FileInputStream("d:\\test.txt"); FileChannel channel = inputStream.getChannel();
|
文件读写
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| File src = new File("d:\\testdd.txt"); File target = new File("d:\\testddTarget.txt"); //if 判断略 FileInputStream fis = null; FileOutputStream fos=null; FileChannel cfis=null; FileChannel cfos=null; try { fis = new FileInputStream(src); fos = new FileOutputStream(target); cfis = fis.getChannel(); cfos = fos.getChannel(); ByteBuffer byteBuffer = ByteBuffer.allocateDirect(1024); while (cfis.read(byteBuffer)>0){ byteBuffer.flip(); //注意,读写转换一定要翻转,否则写出去的是乱码或者空占位 cfos.write(byteBuffer); byteBuffer.clear(); } } catch (Exception e) { e.printStackTrace(); }finally { try { if (cfis.isOpen()) //通道需要关闭,因为通道无法复用 cfis.close(); if (cfos.isOpen()) cfos.close(); fis.close(); fos.close(); } catch (IOException e) { e.printStackTrace(); } } System.out.println("写出成功");
|
5.Channel矢量I/O
Scatter/Gather
简单而强大的概念,可以在多个缓冲区上实现一个简单的I/O操作
对于write而言:数据从几个缓冲区中按顺序抽取(gather)并沿着通道发送
对于read而言:从通道中读取的数据会按顺序散布(Scatter)到多个缓冲区,将每个缓冲区填满直至通道中的数据或者缓冲区最大空间消耗完
注意:缓冲区本身并不具备Scatter/Gather
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| File src = new File("d:\\testdd.txt"); File target = new File("d:\\testddTarget.txt");
FileInputStream fis = null; FileOutputStream fos=null; FileChannel cfis=null; FileChannel cfos=null; try { fis = new FileInputStream(src); fos = new FileOutputStream(target); cfis = fis.getChannel(); cfos = fos.getChannel(); ByteBuffer byteBuffer1 = ByteBuffer.allocateDirect(1024); ByteBuffer byteBuffer2 = ByteBuffer.allocateDirect(1024*16);
ByteBuffer[] byteBuffers={byteBuffer1,byteBuffer2}; //合并
while (cfis.read(byteBuffers)>0){ //Scatter注入 byteBuffer1.flip(); byteBuffer2.flip(); cfos.write(byteBuffers); //gather抽取 byteBuffer1.clear(); byteBuffer2.clear(); } } catch (Exception e) { e.printStackTrace(); }finally { try { if (cfis.isOpen()) cfis.close(); if (cfos.isOpen()) cfos.close(); fis.close(); fos.close(); } catch (IOException e) { e.printStackTrace(); } } System.out.println("写出成功");
|
6.文件通道
对于文件通道来说,总司阻塞式的,因此不能被置于非阻塞模式(由文件I/O性质决定)
对于文件I/O而言,最强大之处在于异步I/O,他可以允许一个进程操作一个或多个I/O操作,即AIO
FileChannel对象是线程安全的,多个进程(进程级别和线程)可以在同一个实例(磁盘文件)上并发调用方法而不会有任何问题
但是影响通道位置和大小的操作都是单线程,这些操作同时最多只有一个执行,再多尝试会产生阻塞
IO VS NIO1 VS NIO2
https://www.jianshu.com/p/1e55c6705392