java中的nio

来源:互联网 发布:淘宝淘金币领取 编辑:程序博客网 时间:2024/06/06 07:39

1.什么是nio

nio是一个非阻塞的IO。之前学的IO是面向流的一种技术。而nio是面向缓冲区的一种技术。采用通道传输。nio可以高效的进行文件的读写操作(之所以速度的提高来自于操作系统所执行的IO方式:缓冲区和通道)。

2.缓冲区

缓冲区的API除了没有boolean对应的Buffer,其他基本类型都有,如IntBuffer,ByteBuffer,FloatBuffer等。

缓冲区的原理:

缓冲区在java nio中负责数据的存取。缓冲区就是数组,用于存取不同类型的数据。

缓冲区的属性:

初始化图:(分配空间为7的容量)


capacity:容量,表示缓冲区中最大存储数据的容量。一旦声明不能改变。

limit:界限,表示缓冲区中可以操作数据的大小。(limit后数据不能读写)
position:位置,表示缓冲区中正在操作数据的位置。

public class TestBuffer {private void printProperties(int position,int limit,int capacity){System.out.println("capacity:"+capacity);System.out.println("limit:"+limit);System.out.println("position:"+position);}       @Test    public void test1(){        String str ="abcde";                //分配一个指定大小的缓冲区        ByteBuffer buf =ByteBuffer.allocate(1024);        System.out.println("----------初始化------------");        printProperties(buf.position(),buf.limit(),buf.capacity());       }}
结果:



put方法:存入元素到缓冲区


System.out.println("-----------put------------");buf.put(str.getBytes());printProperties(buf.position(),buf.limit(),buf.capacity());
结果:



flip方法:用来切换到读模式

buf.flip();printProperties(buf.position(),buf.limit(),buf.capacity());System.out.println("----------get-------------");byte[] dest =new byte[buf.limit()];buf.get(dest);printProperties(buf.position(),buf.limit(),buf.capacity());
结果:



rewind方法:重复读,将位置设置为零并丢弃标记。

System.out.println("----------rewind-------------");buf.rewind();printProperties(buf.position(),buf.limit(),buf.capacity());
结果:



clear方法:清空缓冲区,将位置设置为零,限制设置为该容量,并且丢弃标记。

System.out.println("----------clear-------------");buf.clear();printProperties(buf.position(),buf.limit(),buf.capacity());
结果:


mark和reset方法

mark属性:标记,表示记录当前position的位置。可以通过reset()恢复到mark的位置。reset 恢复到mark标记的位置

@Testpublic void test2(){String str ="efghijk";ByteBuffer buf =ByteBuffer.allocate(1024);printProperties(buf.position(), buf.limit(), buf.capacity());buf.put(str.getBytes());buf.flip();byte[] dest =new byte[buf.limit()];buf.get(dest,0,3);System.out.println(new String(dest,0,2));System.out.println(buf.position());//markbuf.mark();buf.get(dest,2,2);System.out.println(new String(dest,2,2));System.out.println(buf.position());//reset 恢复到mark标记的位置buf.reset();System.out.println(buf.position());}
结果:



直接缓冲区与间接缓冲区的区别:
间接缓冲区:通过allocate方法分配缓冲区,将缓冲区建立在jvm内存中
直接缓冲区:通过allocateDirect方法分配直接缓冲区,将缓冲区建立在操作系统的物理内存中。


3.通道

通道在java nio中负责缓冲区中数据的传输。

主要实现类:

①FileChannel:用于操作本地的通道。

②SocketChannel:用于操作网络TCP协议的通道。

③ServerSocketChannel:用于操作网络TCP协议的通道。

④DatagramChannel:用于操作网络UDP协议的通道。

获取通道:

本地IO方式:FileInputStream/FileOutputStream、RandomAccessFile

网络IO方式:Socket、ServerSocket、DatagramSocket

示例:

方式一:使用文件流来获取通道的方式来复制文件

@Testpublic void testChannel() throws Exception{FileInputStream fis =new FileInputStream("C:\\Users\\Administrator\\Desktop\\触发器.txt");FileOutputStream fos =new FileOutputStream("C:\\Users\\Administrator\\Desktop\\触发器1.txt");FileChannel fic =fis.getChannel();FileChannel foc =fos.getChannel();ByteBuffer buf =ByteBuffer.allocate(1024);while(fic.read(buf) !=-1){buf.flip();foc.write(buf);buf.clear();}foc.close();fic.close();fos.close();fis.close();}
结果:


方式二:将文件区域直接映射到内存中来创建

@Testpublic void testChannelByOpen() throws Exception{FileChannel inChannel =FileChannel.open(Paths.get("C:\\Users\\Administrator\\Desktop\\触发器.txt"), StandardOpenOption.READ);FileChannel outChannel =FileChannel.open(Paths.get("C:\\Users\\Administrator\\Desktop\\触发器2.txt"),StandardOpenOption.WRITE,StandardOpenOption.READ,StandardOpenOption.CREATE);//直接字节缓冲区,其内容是文件的内存映射区域:将此通道的文件区域直接映射到内存中。MappedByteBuffer inMappedBuf =inChannel.map(MapMode.READ_ONLY, 0, inChannel.size());MappedByteBuffer outMapperBuf =outChannel.map(MapMode.READ_WRITE, 0, inChannel.size());//直接对缓冲区进行读写操作byte[] dest =new byte[inMappedBuf.limit()];inMappedBuf.get(dest);outMapperBuf.put(dest);outChannel.close();inChannel.close();}
结果:


4.字符编码与解码

示例:

@Testpublic void testCharset() throws IOException{Charset cs1 =Charset.forName("UTF-8");//获取编码器     CharsetEncoder ce =cs1.newEncoder();     //获取解码器     CharsetDecoder cd =cs1.newDecoder();CharBuffer cBuf =CharBuffer.allocate(1024);cBuf.put("张无忌");cBuf.flip();//编码ByteBuffer eBuf =ce.encode(cBuf);while(eBuf.hasRemaining()){System.out.println(eBuf.get());}//解码eBuf.flip();CharBuffer result =cd.decode(eBuf);System.out.println(result.toString());}
结果:


5.阻塞式网络NIO

客户端发送本地文件数据:

@Testpublic void initClient() throws IOException{//获取网络NIO的通道SocketChannel socketChannel =SocketChannel.open(new InetSocketAddress("localhost", 8888));//获取本地文件对应的文件通道FileChannel inputChannel =FileChannel.open(Paths.get("C:\\Users\\Administrator\\Desktop\\1.jpg"), StandardOpenOption.READ);//分配1024大小的缓冲区ByteBuffer buf =ByteBuffer.allocate(1024);//循环读取本地文件通道中的缓冲区中的数据,写入到网络通道缓冲区中while(inputChannel.read(buf) !=-1){buf.flip();socketChannel.write(buf);buf.clear();}socketChannel.shutdownOutput();//发送成功提示int len =0;while((len =socketChannel.read(buf)) !=-1){buf.flip();System.out.println(new String(buf.array(),0,len));buf.clear();}inputChannel.close();socketChannel.close();}
服务端接收数据:

@Testpublic void initServer() throws IOException{    ServerSocketChannel ssChannel =ServerSocketChannel.open();    FileChannel outChannel =FileChannel.open(Paths.get("C:\\Users\\Administrator\\Desktop\\2.jpg"), StandardOpenOption.WRITE,StandardOpenOption.CREATE);    ssChannel.bind(new InetSocketAddress(8888));            SocketChannel socketChannel =ssChannel.accept();    ByteBuffer buf =ByteBuffer.allocate(1024);    while(socketChannel.read(buf) !=-1){        buf.flip();        outChannel.write(buf);        buf.clear();    }            buf.put("服务端接收客户端的数据成功".getBytes());    buf.flip();    socketChannel.write(buf);            socketChannel.close();    outChannel.close();    ssChannel.close();}
结果:



6.非阻塞网络NIO

客户端发送数据:

@Testpublic void initClient() throws IOException{SocketChannel socketChannel =SocketChannel.open(new InetSocketAddress("localhost", 8989));socketChannel.configureBlocking(false);ByteBuffer buf =ByteBuffer.allocate(1024);Scanner scanner =new Scanner(System.in);while(scanner.hasNext()){String content =scanner.next();buf.put((new Date()+" : "+content).getBytes());buf.flip();socketChannel.write(buf);buf.clear();}socketChannel.close();}
服务端接收数据:

@Testpublic void initServer() throws IOException{ServerSocketChannel serverSocketChannel =ServerSocketChannel.open();serverSocketChannel.configureBlocking(false);serverSocketChannel.bind(new InetSocketAddress(8989));Selector selector =Selector.open();serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);while(selector.select() >0){Iterator<SelectionKey> it =selector.selectedKeys().iterator();while(it.hasNext()){SelectionKey key =it.next();if(key.isAcceptable()){SocketChannel socketChannel =serverSocketChannel.accept();socketChannel.configureBlocking(false);socketChannel.register(selector, SelectionKey.OP_READ);}else if(key.isReadable()){SocketChannel socketChannel =(SocketChannel) key.channel();ByteBuffer buf =ByteBuffer.allocate(1024);int len =0;while((len =socketChannel.read(buf))>0){buf.flip();System.out.println(new String(buf.array(),0,len));buf.clear();}}it.remove();}}}
结果:





7.使用UDP协议的网络NIO

服务端:

@Testpublic void server() throws IOException{DatagramChannel dc = DatagramChannel.open();dc.configureBlocking(false);dc.bind(new InetSocketAddress(9898));Selector selector = Selector.open();dc.register(selector, SelectionKey.OP_READ);while(selector.select() > 0){Iterator<SelectionKey> it = selector.selectedKeys().iterator();while(it.hasNext()){SelectionKey sk = it.next();if(sk.isReadable()){ByteBuffer buf = ByteBuffer.allocate(1024);dc.receive(buf);buf.flip();System.out.println(new String(buf.array(), 0, buf.limit()));buf.clear();}}it.remove();}}
客户端:

@Testpublic void client() throws IOException{DatagramChannel dc = DatagramChannel.open();dc.configureBlocking(false);ByteBuffer buf = ByteBuffer.allocate(1024);Scanner scan = new Scanner(System.in);while(scan.hasNext()){String str = scan.next();buf.put((new Date().toString() + " : " + str).getBytes());buf.flip();dc.send(buf, new InetSocketAddress("localhost", 9898));buf.clear();}dc.close();}
结果: