通道(Channel) 基本介绍
NIO的通道类似于流,但有些区别如下: • 通道可以同时进行读写,而流只能读或者只能写 • 通道可以实现异步读写数据 • 通道可以从缓冲读数据,也可以写数据到缓冲
BIO 中的 stream 是单向的,例如 FileInputStream 对象只能进行读取数据的操作,而 NIO 中的通道 (Channel)是双向的,可以读操作,也可以写操作。
Channel在NIO中是一个接口public interface Channel extends Closeable{}
常用的 Channel 类有:FileChannel、DatagramChannel、ServerSocketChannel 和SocketChannel。【ServerSocketChanne 类似ServerSocket , SocketChannel 类似 Socket】
FileChannel 用于文件的数据读写,DatagramChannel 用于 UDP 的数据读写,ServerSocketChannel 和 SocketChannel 用于 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),把数据从当前通道复制给目标通道
文件写数据 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 package com.jhj .channel ; import java.io .FileNotFoundException ;import java.io .FileOutputStream ;import java.io .IOException ;import java.nio .ByteBuffer ;import java.nio .channels .FileChannel ;public class NioFileChannel { private static ByteBuffer allocate; public static void main (String [] args) throws IOException { String hello = "hello" ; FileOutputStream fileOutputStream = new FileOutputStream ("F:\\1.txt" );; FileChannel channel = fileOutputStream.getChannel (); ByteBuffer allocate = ByteBuffer .allocate (1024 ); allocate.put (hello.getBytes ()); allocate.flip (); channel.write (allocate); fileOutputStream.close (); } }
文件读数据 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 package com.jhj .channel ; import java.io .File ;import java.io .FileInputStream ;import java.io .FileOutputStream ;import java.io .IOException ;import java.nio .ByteBuffer ;import java.nio .channels .FileChannel ;public class NioFileChannel02 { private static ByteBuffer allocate; public static void main (String [] args) throws IOException { File file = new File ("F:\\1.txt" ); FileInputStream fileInputStream = new FileInputStream (file); FileChannel channel = fileInputStream.getChannel (); ByteBuffer allocate1 = ByteBuffer .allocate ((int) file.length ()); channel.read (allocate1); System .out .println (new String (allocate1.array ())); fileInputStream.close (); } }
一个buffer 完成读写 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 41 42 43 44 45 46 47 48 package com.jhj .channel ; import java.io .File ;import java.io .FileInputStream ;import java.io .FileOutputStream ;import java.io .IOException ;import java.nio .ByteBuffer ;import java.nio .channels .FileChannel ;public class NioFileChannel03 { private static ByteBuffer allocate; public static void main (String [] args) throws IOException { FileInputStream fileInputStream = new FileInputStream ("F://1.txt" ); FileChannel channel = fileInputStream.getChannel (); FileOutputStream fileOutputStream = new FileOutputStream ("F://2.txt" ); FileChannel channel1 = fileOutputStream.getChannel (); ByteBuffer allocate1 = ByteBuffer .allocate (1024 ); while (true ){ allocate1.clear (); int read = channel.read (allocate1); if (read == -1 ) { break ; } allocate1.flip (); channel1.write (allocate1); } fileInputStream.close (); fileOutputStream.close (); } }
文件拷贝 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 package com.jhj .channel ; import java.io .FileInputStream ;import java.io .FileOutputStream ;import java.io .IOException ;import java.nio .ByteBuffer ;import java.nio .channels .FileChannel ;public class NioFileChannel04 { private static ByteBuffer allocate; public static void main (String [] args) throws IOException { FileInputStream fileInputStream = new FileInputStream ("F://1.txt" ); FileChannel channel = fileInputStream.getChannel (); FileOutputStream fileOutputStream = new FileOutputStream ("F://2.txt" ); FileChannel channel1 = fileOutputStream.getChannel (); channel1.transferFrom (channel,0 ,channel.size ()); fileInputStream.close (); fileOutputStream.close (); } }
关于Buffer 和 Channel的注意事项和细节
ByteBuffer 支持类型化的put 和 get, put 放入的是什么数据类型,get就应该使用相应的数据类型来取出,否则可能有 BufferUnderflowException 异常。顺序要一致
可以将一个普通Buffer 转成只读Buffer buffer.asReaadOnlyBuffer()
NIO 还提供了 MappedByteBuffer, 可以让文件直接在内存(堆外的内存)中进行修改, 而如何同步到文件由NIO 来完成.
前面我们讲的读写操作,都是通过一个Buffer 完成的,NIO 还支持 通过多个Buffer (即 Buffer 数组) 完成读写操作,即 Scattering 和 Gathering
MappedByteBuffer 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 package com.jhj .channel ; import java.io .*;import java.nio .ByteBuffer ;import java.nio .MappedByteBuffer ;import java.nio .channels .FileChannel ;public class NioFileChannel05 { public static void main (String [] args) throws Exception { RandomAccessFile rw = new RandomAccessFile ("F://1.txt" , "rw" ); FileChannel channel = rw.getChannel (); MappedByteBuffer map = channel.map (FileChannel .MapMode .READ_WRITE , 0 , 5 ); map.put (0 ,(byte)'J' ); map.put (3 ,(byte)'J' ); rw.close (); System .out .println ("修改成功" ); } }
buffer数组 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 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 package com.jhj .channel ; import java.io .RandomAccessFile ;import java.lang .reflect .Array ;import java.net .InetSocketAddress ;import java.nio .ByteBuffer ;import java.nio .MappedByteBuffer ;import java.nio .channels .FileChannel ;import java.nio .channels .ServerSocketChannel ;import java.nio .channels .SocketChannel ;import java.util .Arrays ;public class NioFileChannel06 { public static void main (String [] args) throws Exception { ServerSocketChannel open = ServerSocketChannel .open (); InetSocketAddress inetSocketAddress = new InetSocketAddress (7000 ); open.socket ().bind (inetSocketAddress); ByteBuffer [] byteBuffers = new ByteBuffer [2 ]; byteBuffers[0 ]=ByteBuffer .allocate (5 ); byteBuffers[1 ]=ByteBuffer .allocate (3 ); SocketChannel accept = open.accept (); int messageLength=8 ; while (true ){ int byteRead=0 ; while (byteRead<messageLength){ long read = accept.read (byteBuffers); byteRead+=read; System .out .println ("byteRead=" +byteRead); Arrays .asList (byteBuffers).stream ().map (buffer-> "position" +buffer.position ()+"limit=" +buffer.limit () ).forEach (System .out ::println); Arrays .asList (byteBuffers).forEach (byteBuffer -> byteBuffer.flip ()); long byteWrite=0 ; while (byteWrite<messageLength){ long write = accept.write (byteBuffers); byteWrite+=write; } Arrays .asList (byteBuffers).forEach (byteBuffer -> byteBuffer.clear ()); System .out .println ("byteRead:=" +byteRead+"byteWrite:=" +byteWrite+"messageLength:=" +messageLength); } } } }
作者声明