NIO支持缓冲区和通道,效率非常高,非常好用,代码演示如下 1.NIO的HelloWorld
package cn.zzu.wcj.nio;import static org.junit.Assert.*;import java.nio.ByteBuffer;import org.junit.Test;/* * 一、缓冲区(Buffer):在 Java NIO 中负责数据的存取。缓冲区就是数组。用于存储不同数据类型的数据 * * 根据数据类型不同(boolean 除外),提供了相应类型的缓冲区: * ByteBuffer * CharBuffer * ShortBuffer * IntBuffer * LongBuffer * FloatBuffer * DoubleBuffer * * 上述缓冲区的管理方式几乎一致,通过 allocate() 获取缓冲区 * * 二、缓冲区存取数据的两个核心方法: * put() : 存入数据到缓冲区中 * get() : 获取缓冲区中的数据 * * 三、缓冲区中的四个核心属性: * capacity : 容量,表示缓冲区中最大存储数据的容量。一旦声明不能改变。 * limit : 界限,表示缓冲区中可以操作数据的大小。(limit 后数据不能进行读写) * position : 位置,表示缓冲区中正在操作数据的位置。 * * mark : 标记,表示记录当前 position 的位置。可以通过 reset() 恢复到 mark 的位置 * * 0 <= mark <= position <= limit <= capacity * * 四、直接缓冲区与非直接缓冲区: * 非直接缓冲区:通过 allocate() 方法分配缓冲区,将缓冲区建立在 JVM 的内存中 * 直接缓冲区:通过 allocateDirect() 方法分配直接缓冲区,将缓冲区建立在物理内存中。可以提高效率 */public class BufferTest { @Test public void test1() { String str="abcde" ; //1.分配一个指定大小的缓冲区 ByteBuffer buf=ByteBuffer.allocate(1024) ; System.out.PRintln("-----------------allocate()------------------"); System.out.println("position="+buf.position()); System.out.println("limit="+buf.limit()); System.out.println("capacity="+buf.capacity()); //2.利用put()方法存入数据 buf.put(str.getBytes()) ; System.out.println("-----------------put()------------------"); System.out.println("position="+buf.position()); System.out.println("limit="+buf.limit()); System.out.println("capacity="+buf.capacity()); //3.切换到读取模式 buf.flip() ; System.out.println("-----------------flip()------------------"); System.out.println("position="+buf.position()); System.out.println("limit="+buf.limit()); System.out.println("capacity="+buf.capacity()); //4.利用get()读取缓冲区中的数据 byte[] dst=new byte[buf.limit()] ; buf.get(dst,buf.position(),buf.limit()) ; System.out.println("-----------------get()------------------"); System.out.println("position="+buf.position()); System.out.println("limit="+buf.limit()); System.out.println("capacity="+buf.capacity()); //5.rewind()可重复读 buf.rewind() ; System.out.println("-----------------rewind()------------------"); System.out.println("position="+buf.position()); System.out.println("limit="+buf.limit()); System.out.println("capacity="+buf.capacity()); //6.clear():清空缓冲区,但是缓冲区中的数据依然存在,但是处于‘被遗忘’状态 buf.clear() ; System.out.println("-----------------clear()------------------"); System.out.println("position="+buf.position()); System.out.println("limit="+buf.limit()); System.out.println("capacity="+buf.capacity()); System.out.println((char)buf.get(0)); } @Test public void test2(){ String str="abcde" ; ByteBuffer buf=ByteBuffer.allocate(1024) ; byte[] source=str.getBytes() ; buf.put(source, 0,2) ; System.out.println("position="+buf.position()); buf.mark() ; System.out.println("-------------mark()@2--------------"); buf.put(source, 2, 2) ; System.out.println("position="+buf.position()); System.out.println("----------------reset()--------------------"); buf.reset() ; System.out.println("position="+buf.position());// System.out.println("len="+source.length); if(buf.hasRemaining()){ System.out.println(buf.remaining()); } } @Test public void test3(){ ByteBuffer buf=ByteBuffer.allocateDirect(1024) ; assertSame(true, buf.isDirect()); }}2.创建Channel的几种方式
package cn.zzu.wcj.nio;import java.io.FileInputStream;import java.io.FileOutputStream;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.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() * transferTo() * * 五、分散(Scatter)与聚集(Gather) * 分散读取(Scattering Reads):将通道中的数据分散到多个缓冲区中 * 聚集写入(Gathering Writes):将多个缓冲区中的数据聚集到通道中 * * 六、字符集:Charset * 编码:字符串 -> 字节数组 * 解码:字节数组 -> 字符串 * */public class ChannelTest { @Test public void testEncDec() throws Exception{ Charset charset = Charset.forName("GBK") ; CharsetEncoder encoder = charset.newEncoder(); CharsetDecoder decoder = charset.newDecoder() ; CharBuffer charBuf=CharBuffer.allocate(1024) ; charBuf.put("HelloWorld,世界你好!"); charBuf.flip() ; ByteBuffer byteBuffer = encoder.encode(charBuf); for(int x=0;x<byteBuffer.limit();x++){ System.out.print(byteBuffer.get()+"、"); } System.out.println(); byteBuffer.flip() ; CharBuffer charBuffer = decoder.decode(byteBuffer); System.out.println(charBuffer.toString()); System.out.println("-----------------------------");// Charset charset2 = Charset.forName("UTF-8") ; Charset charset2 = Charset.forName("GBK") ; byteBuffer.flip() ; CharBuffer charBuffer2 = charset2.decode(byteBuffer) ; System.out.println(charBuffer2.toString()); } @Test public void testCharset(){ Map<String,Charset> charsets = Charset.availableCharsets() ; Set<Entry<String,Charset>> set = charsets.entrySet() ; for(Entry<String,Charset> e : set ){ System.out.println(e.getKey()+"="+e.getValue()); } } @Test public void testScatterAndGather()throws Exception{ RandomAccessFile raf=new RandomAccessFile("1.txt", "rw") ; FileChannel inChannel = raf.getChannel() ; ByteBuffer buf=ByteBuffer.allocate(100) ; ByteBuffer buf2=ByteBuffer.allocate(1024) ; ByteBuffer bufs[]={buf,buf2} ; inChannel.read(bufs) ; for(ByteBuffer byteBuf : bufs){ byteBuf.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())); RandomAccessFile raf2=new RandomAccessFile("2.txt", "rw") ; FileChannel outChannel = raf2.getChannel() ; outChannel.write(bufs) ; outChannel.close(); inChannel.close(); raf.close(); raf2.close(); } @Test public void testChannel() throws Exception{ long start=System.currentTimeMillis() ; FileInputStream fis=null ; FileOutputStream fos=null ; fis=new FileInputStream("1.jpg"); fos=new FileOutputStream("2.jpg") ; //1.获取通道 FileChannel inChannel = fis.getChannel(); FileChannel outChannel = fos.getChannel(); //2.准备缓冲区 ByteBuffer buf=ByteBuffer.allocate(1024) ; //3.读写 while(inChannel.read(buf) != -1){ //读 buf.flip() ; //切换到读取模式 outChannel.write(buf) ; //写 buf.clear() ; //清空缓冲区,准备再次读取 } //4.关闭流 outChannel.close(); inChannel.close(); fos.close(); fis.close(); long end=System.currentTimeMillis() ; System.out.println("拷贝任务耗时:"+(end-start)+" 毫秒"); } @Test public void testDirectChannel()throws Exception{ long start=System.currentTimeMillis() ; FileChannel inChannel = FileChannel.open(Paths.get("1.jpg"), StandardOpenOption.READ) ; FileChannel outChannel = FileChannel.open(Paths.get("2.jpg"), StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.CREATE ) ; //内存映射文件 MappedByteBuffer inMappedBuf = inChannel.map(MapMode.READ_ONLY, 0, inChannel.size()) ; MappedByteBuffer outMappedBuf = outChannel.map(MapMode.READ_WRITE, 0, inChannel.size()) ; //直接对缓冲区中的数据进行读写操作 byte[] temp=new byte[1024] ; inMappedBuf.get(temp) ; outMappedBuf.put(temp) ; inChannel.close(); outChannel.close(); long end=System.currentTimeMillis() ; System.out.println("拷贝任务耗时:"+(end-start)+" 毫秒"); } @Test public void testTransform()throws Exception{ FileChannel inChannel = FileChannel.open(Paths.get("1.jpg"), StandardOpenOption.READ) ; FileChannel outChannel = FileChannel.open(Paths.get("4.jpg"), StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.CREATE ) ; //inChannel.transferTo(0, inChannel.size(), outChannel) ; outChannel.transferFrom(inChannel, 0, inChannel.size()) ; inChannel.close(); outChannel.close(); }}3.阻塞式NIO
package cn.zzu.wcj.nio;import java.io.IOException;import java.net.InetSocketAddress;import java.nio.ByteBuffer;import java.nio.channels.FileChannel;import java.nio.channels.ServerSocketChannel;import java.nio.channels.SocketChannel;import java.nio.file.Paths;import java.nio.file.StandardOpenOption;import org.junit.Test;/* * 一、使用 NIO 完成网络通信的三个核心: * * 1. 通道(Channel):负责连接 * * java.nio.channels.Channel 接口: * |--SelectableChannel * |--SocketChannel * |--ServerSocketChannel * |--DatagramChannel * * |--Pipe.SinkChannel * |--Pipe.SourceChannel * * 2. 缓冲区(Buffer):负责数据的存取 * * 3. 选择器(Selector):是 SelectableChannel 的多路复用器。用于监控 SelectableChannel 的 IO 状况 * */public class BlockingNIOTest { @Test public void testClient() throws Exception { //1.创建通道 SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 8989)); //2.准备缓冲区 FileChannel inChannel = FileChannel.open(Paths.get("1.jpg"), StandardOpenOption.READ) ; ByteBuffer buf=ByteBuffer.allocate(1024) ; //3.读取本地文件,发送到客户端 while(inChannel.read(buf) != -1){ buf.flip() ; socketChannel.write(buf) ; buf.clear() ; } //4.关闭通道 inChannel.close(); socketChannel.close(); } @Test public void testServer() throws Exception{ //1.创建通道 ServerSocketChannel serverSocketChannel = ServerSocketChannel.open() ; //2.绑定端口号 serverSocketChannel.bind(new InetSocketAddress(8989)) ; //3.准备Channel和Buffer FileChannel outChannel=FileChannel.open(Paths.get("2.jpg"), StandardOpenOption.WRITE,StandardOpenOption.CREATE) ; ByteBuffer buf=ByteBuffer.allocate(1024) ; //4.接收客户端请求 SocketChannel socketChannel = serverSocketChannel.accept() ; while(socketChannel.read(buf) != -1){ buf.flip() ; outChannel.write(buf) ; buf.clear() ; } //5.关闭流 socketChannel.close(); outChannel.close(); serverSocketChannel.close(); }}package cn.zzu.wcj.nio;import java.net.InetSocketAddress;import java.nio.ByteBuffer;import java.nio.channels.FileChannel;import java.nio.channels.ServerSocketChannel;import java.nio.channels.SocketChannel;import java.nio.file.Paths;import java.nio.file.StandardOpenOption;import org.junit.Test;public class BlockingNIOTestPlus { @Test public void testClent() throws Exception{ SocketChannel client = SocketChannel.open(new InetSocketAddress("127.0.0.1", 9999)) ; FileChannel inChannel = FileChannel.open(Paths.get("1.jpg"), StandardOpenOption.READ) ; ByteBuffer buf=ByteBuffer.allocate(1024) ; while(inChannel.read(buf) != -1){ buf.flip() ; client.write(buf) ; buf.clear() ; } client.shutdownOutput() ; //结束输出 //接收服务器反馈 while(client.read(buf) != -1){ buf.flip() ; System.out.println(new String(buf.array(),0,buf.limit())); buf.clear() ; } inChannel.close() ; client.close() ; } @Test public void testServer() throws Exception{ ServerSocketChannel server = ServerSocketChannel.open() ; server.bind(new InetSocketAddress(9999)) ; FileChannel outChannel = FileChannel.open(Paths.get("2.jpg"), StandardOpenOption.WRITE, StandardOpenOption.CREATE ) ; ByteBuffer buf=ByteBuffer.allocate(1024) ; SocketChannel client = server.accept(); while(client.read(buf)!=-1){ buf.flip() ; outChannel.write(buf) ; buf.clear() ; } //发送反馈给客户端 buf.put("乖儿子,爸爸接收到黄图啦!!!".getBytes()) ; buf.flip() ; client.write(buf) ; client.close(); outChannel.close(); server.close(); }}4.非阻塞式NIO
package cn.zzu.wcj.nio;import java.io.IOException;import java.net.InetSocketAddress;import java.nio.ByteBuffer;import java.nio.channels.SelectionKey;import java.nio.channels.Selector;import java.nio.channels.ServerSocketChannel;import java.nio.channels.SocketChannel;import java.text.SimpleDateFormat;import java.util.Date;import java.util.Iterator;import java.util.Scanner;import org.junit.Test;/* * 一、使用 NIO 完成网络通信的三个核心: * * 1. 通道(Channel):负责连接 * * java.nio.channels.Channel 接口: * |--SelectableChannel * |--SocketChannel * |--ServerSocketChannel * |--DatagramChannel * * |--Pipe.SinkChannel * |--Pipe.SourceChannel * * 2. 缓冲区(Buffer):负责数据的存取 * * 3. 选择器(Selector):是 SelectableChannel 的多路复用器。用于监控 SelectableChannel 的 IO 状况 * */public class TestNonBlockingChannel { @Test public void testClient() throws Exception{ //创建客户端通道 SocketChannel sChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1",6666)) ; //配置为非阻塞式NIO sChannel.configureBlocking(false) ; //准备缓冲区 ByteBuffer buffer = ByteBuffer.allocate(1024) ; Scanner sc=new Scanner(System.in) ; while(sc.hasNext()){ buffer.put((new Date().toString()+" : "+sc.next()).getBytes()) ; buffer.flip() ; //切换成读模式 //向服务器端发送消息 sChannel.write(buffer) ; buffer.clear() ; } //关闭通道 sChannel.close(); } @Test public void testServer() throws Exception{ //1.创建服务器端通道 ServerSocketChannel ssChannel = ServerSocketChannel.open() ; //2.绑定端口号 ssChannel.bind(new InetSocketAddress(6666)) ; //3.设置非阻塞模式 ssChannel.configureBlocking(false) ; //4.获取选择器 Selector selector = Selector.open() ; //5.将选择器注册到通道上 ssChannel.register(selector,SelectionKey.OP_ACCEPT) ; //6.以轮训的方式获取选择器上已经准备就绪的事件 while(selector.select() > 0){ //7.接收全部选择键 Iterator<SelectionKey> iterator = selector.selectedKeys().iterator(); while(iterator.hasNext()){ //8.接收选择键 SelectionKey selectionKey = iterator.next(); //9.根据键值判断具体是什么事件 if(selectionKey.isAcceptable()){ //10.接收就绪,获取客户端连接 SocketChannel sChannel = ssChannel.accept() ; //11.切换到非阻塞模式 sChannel.configureBlocking(false) ; //12.将通道注册到选择器上 sChannel.register(selector, SelectionKey.OP_READ) ; }else if(selectionKey.isReadable()){ //13.获取读状态的通道 SocketChannel sChannel=(SocketChannel) selectionKey.channel() ; ByteBuffer dst=ByteBuffer.allocate(1024) ; //14.读取数据 Integer length=0 ; while( (length=sChannel.read(dst))> 0){ dst.flip() ; System.out.println(new String(dst.array(),0,length)); dst.clear() ; } } //15.取消选择键 iterator.remove(); } } }}package cn.zzu.wcj.nio;import java.net.InetSocketAddress;import java.nio.ByteBuffer;import java.nio.channels.DatagramChannel;import java.nio.channels.SelectionKey;import java.nio.channels.Selector;import java.util.Date;import java.util.Iterator;import java.util.Scanner;import org.junit.Test;public class TestNonBlockingNIOPlus { @Test public void testSend() throws Exception{ DatagramChannel dc=DatagramChannel.open() ; dc.configureBlocking(false) ; ByteBuffer buf=ByteBuffer.allocate(1024) ; Scanner sc=new Scanner(System.in) ; while(sc.hasNext()){ String msg=sc.next() ; buf.put((new Date().toString()+" : "+msg).getBytes()) ; buf.flip() ; dc.send(buf, new InetSocketAddress("127.0.0.1",9999)) ; buf.clear() ; } dc.close(); } @Test public void testReceive() throws Exception{ DatagramChannel dc = DatagramChannel.open() ; dc.configureBlocking(false) ; dc.bind(new InetSocketAddress(9999)) ; Selector selector = Selector.open() ; dc.register(selector, SelectionKey.OP_READ) ; while(selector.select()>0){ Iterator<SelectionKey> iterator = selector.selectedKeys().iterator() ; while(iterator.hasNext()){ SelectionKey selectionKey = iterator.next(); if(selectionKey.isReadable()){ ByteBuffer buf=ByteBuffer.allocate(1024) ; dc.receive(buf) ; buf.flip() ; System.out.println(new String(buf.array(),0,buf.limit())); buf.clear() ; } } iterator.remove(); } }}5.管道
package cn.zzu.wcj.nio;import java.io.IOException;import java.nio.ByteBuffer;import java.nio.channels.Pipe;import java.nio.channels.Pipe.SinkChannel;import java.nio.channels.Pipe.SourceChannel;import org.junit.Test;public class PipeTest { @Test public void testPipe() throws Exception{ //1.获取管道 Pipe pipe = Pipe.open() ; SinkChannel sinkChannel = pipe.sink(); //2.准备缓冲区 ByteBuffer buf=ByteBuffer.allocate(1024) ; buf.put("单向管道发送数据".getBytes()) ; buf.flip() ; //4.发送数据 sinkChannel.write(buf) ; buf.clear() ; //5.接收数据 SourceChannel sourceChannel = pipe.source() ; sourceChannel.read(buf) ; buf.flip() ; System.out.println(new String(buf.array(),0,buf.limit())); }}新闻热点
疑难解答