当前位置: 代码迷 >> 综合 >> NIO:基本操作和学习
  详细解决方案

NIO:基本操作和学习

热度:14   发布时间:2024-02-09 02:36:00.0

NIO的核心:缓冲区,通道,选择器 

 

Table of Contents

一、缓冲区 Buffer  :存储数据

1 基本概念:

2 直接缓冲区和非直接缓冲区

3 种类:基本数据类型处理boolean外都有

4 常用方法

二、管道channel  : 传输数据

1 概念

2 通道的主要实现类

3 获取的三种方式

 例子:通过非直接缓冲区完成文件复制

 通过直接缓冲区完成文件复制(内存映射文件)

4  通道之间传输 transferTo和 transferFrom

5 分散(scatter)和聚集(Gather)

6 字符集charset

三、阻塞io和非阻塞io

1 阻塞io,就是服务端一直等待客户端的请求,不能去做其他的事情

2 示例

   实例一

示例二

2 非阻塞io,服务端可以在选择器上监控已经准备就绪的事件处理,不用一直等待

示例一

示例二

四、管道:


一、缓冲区 Buffer  :存储数据

1 基本概念:

  1 position :当前操作数据的位置,指针位置

   2 limit:缓冲区中可以操作数据的大小,即limit后面的数据不能操作

   3 capacity:缓冲区的存储大小

2 直接缓冲区和非直接缓冲区

 

   非直接缓冲区(allocate())方法分配的缓冲区,将缓冲区直接建立在jvm上面

   直接缓冲区:allocateDirect()方法分配的缓冲区,将缓冲区直接建立在磁盘内存中,可以提高效率,弊端是消耗大,不安全

ByteBuffer.allocateDirect(1024);
ByteBuffer allocate = ByteBuffer.allocate(1024);

3 种类:基本数据类型处理boolean外都有

ByteBuffer
CharBuffer

ShortBuffer

IntBuffer

LongBuffer

FloatBuffer
?????DoubleBuffer

4 常用方法

 

常用方法:
put  存储数据
get  获取数据
flip:转换为读取模式,将指针位置置于开始部位可以读或者写操作
clear 清空位子,数据还在mark 标记此刻数据的位子
reset  回到mark标记的数据位子
 ByteBuffer byteBuffer = ByteBuffer.allocateDirect(1024);ByteBuffer allocate = ByteBuffer.allocate(1024);System.out.println(allocate.position());System.out.println(allocate.limit());System.out.println(allocate.capacity());//存数据String str = "abcde";allocate.put(str.getBytes());System.out.println("*************put***********");System.out.println(allocate.position());System.out.println(allocate.limit());System.out.println(allocate.capacity());System.out.println("******************flip************");allocate.flip();//切换为读数据模式System.out.println("*************put***********");System.out.println(allocate.position());System.out.println(allocate.limit());System.out.println(allocate.capacity());System.out.println("**********get***************");byte[] dest = new byte[allocate.limit()];allocate.get(dest);System.out.println(new String(dest,0,dest.length));System.out.println(allocate.position());System.out.println(allocate.limit());System.out.println(allocate.capacity());System.out.println("*************clear******");allocate.clear();System.out.println(allocate.position());System.out.println(allocate.limit());System.out.println(allocate.capacity());System.out.println("***********mark**********");allocate.mark();System.out.println(allocate.position());System.out.println(allocate.limit());System.out.println(allocate.capacity());allocate.reset();

 

二、管道channel  : 传输数据

1 概念

    表示IO源与目标打开的连接,类似于传统的流,只不过channel本身不存储数据,它只能与缓冲区交互。

用于源节点和目标节点的连接,在java NIO中负责缓冲区中数据的传输。

2 通道的主要实现类

java.nio.channels.Channel接口-- FileChannel-- SocketChannel-- ServerSocketChannel-- DatagramChannel-- Pipe.SourceChannel--Pipe.SinkChannel

3 获取的三种方式

 

1 java针对支持通道的类提供了getChannel()方法
本地IOFileInputStream/FileOutputStreamRandomAccessFile网络IOsocketServerSocketDatagramSocket比如:
RandomAccessFile raf2 = new RandomAccessFile("2.jpg", "rw"); FileChannel outChannel = raf2.getChannel();2 在jdk1.7 中NIO2针对各个通道提供了静态方法open():

比如:

FileChannel.open(Paths.get("G:\\360Downloads\\photos\\0.jpg"),StandardOpenOption.READ);
3 在jdk1.7 中NIO2的Files 工具类的newByteChannel()

 例子:通过非直接缓冲区完成文件复制

 /*** 利用通道完成数据复制*/@Testpublic void test01(){FileInputStream inputStream =  null;FileOutputStream outputStream = null;FileChannel inChannel = null;FileChannel outChannel = null;try {inputStream = new FileInputStream("src/1.jpg");outputStream = new FileOutputStream("2.jpg");//1 获取通道inChannel = inputStream.getChannel();outChannel = outputStream.getChannel();//2 分配指定大小的缓冲区ByteBuffer buff = ByteBuffer.allocate(1024);//3 将通道的数据读入缓冲区while(inChannel.read(buff) != -1){buff.flip();//4 将缓冲区的数据写入通道outChannel.write(buff);buff.clear();}//5 关闭流和通道} catch (IOException e) {e.printStackTrace();} finally {if(inChannel != null) {try {inChannel.close();} catch (IOException e) {e.printStackTrace();}}if(outChannel!=null){try {outChannel.close();} catch (IOException e) {e.printStackTrace();}}if(outputStream != null) {try {outputStream.close();} catch (IOException e) {e.printStackTrace();}}if(inputStream!=null){try {inputStream.close();} catch (IOException e) {e.printStackTrace();}}}}

 

 通过直接缓冲区完成文件复制(内存映射文件)

  /*** 使用直接缓冲区完成文件复制(内存映射文件)*/@Testpublic void test02() throws Exception {FileChannel inChannel = FileChannel.open(Paths.get("G:\\360Downloads\\photos\\0.jpg"),StandardOpenOption.READ);FileChannel outChannel = FileChannel.open(Paths.get("G:\\360Downloads\\photos\\00.jpg"), StandardOpenOption.READ, StandardOpenOption.WRITE,StandardOpenOption.CREATE);//内存映射文件MappedByteBuffer inBuffer =  inChannel.map(FileChannel.MapMode.READ_ONLY, 0, inChannel.size());MappedByteBuffer outBuffer = outChannel.map(FileChannel.MapMode.READ_WRITE, 0, inChannel.size());//直接对缓冲区进行数据读写操作byte[] dst = new byte[inBuffer.limit()];inBuffer.get(dst);outBuffer.put(dst);outChannel.close();inChannel.close();}

4  通道之间传输 transferTo和 transferFrom

 /** 通道之间传输数据* transferTo* transferFrom*/@Testpublic void test03() throws IOException {FileChannel inChannel = FileChannel.open(Paths.get("G:\\360Downloads\\photos\\0.jpg"),StandardOpenOption.READ);FileChannel outChannel = FileChannel.open(Paths.get("G:\\360Downloads\\photos\\00.jpg"), StandardOpenOption.READ, StandardOpenOption.WRITE,StandardOpenOption.CREATE);//transferTo
//        inChannel.transferTo(0,inChannel.size(),outChannel);//transferFromoutChannel.transferFrom(inChannel,0,inChannel.size());inChannel.close();outChannel.close();}

5 分散(scatter)和聚集(Gather)

/*** 分散和聚集*/@Testpublic void test04() throws Exception {//1 获取通道RandomAccessFile raf1 = new RandomAccessFile("1.jpg", "rw");FileChannel inChannel = raf1.getChannel();//2 分配指定缓冲区ByteBuffer buffer1 = ByteBuffer.allocate(100);ByteBuffer buffer2 = ByteBuffer.allocate(102400);ByteBuffer buffer3 = ByteBuffer.allocate(102400);ByteBuffer buffer4 = ByteBuffer.allocate(102400);//3 分散du读1取ByteBuffer[] dst = {buffer1,buffer2,buffer3,buffer4};inChannel.read(dst);for (ByteBuffer b : dst){b.flip();}// 4 聚集写出RandomAccessFile raf2 = new RandomAccessFile("2.jpg", "rw");FileChannel outChannel = raf2.getChannel();outChannel.write(dst);outChannel.close();inChannel.close();}

6 字符集charset

 @Testpublic void test07() throws CharacterCodingException {Charset charset = Charset.forName("GBK");//获取编码器和解解码器CharsetEncoder encoder = charset.newEncoder();CharsetDecoder decoder = charset.newDecoder();CharBuffer buffer = CharBuffer.allocate(1024);buffer.put("研发费分为非") ;buffer.flip();//编码ByteBuffer byteBuffer = encoder.encode(buffer);//解码CharBuffer charBuffer = decoder.decode(byteBuffer);//读取System.out.println(charBuffer.toString());}

三、阻塞io和非阻塞io

1 阻塞io,就是服务端一直等待客户端的请求,不能去做其他的事情

2 示例

   实例一

 @Testpublic void client() throws IOException {//1 获取通道SocketChannel skChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1",9898));FileChannel fileChannel = FileChannel.open(Paths.get("1.jpg"), StandardOpenOption.READ);//2 获取缓冲区ByteBuffer buffer = ByteBuffer.allocate(1024);//3 通道传输出去数据while(fileChannel.read(buffer) != -1){buffer.flip();skChannel.write(buffer);buffer.clear();}//4 关闭流fileChannel.close();skChannel.close();}@Testpublic void server() throws IOException {// 获取通道ServerSocketChannel sskChannel = ServerSocketChannel.open();// 绑定连接sskChannel.bind(new InetSocketAddress(9898));//获取客户端连接通道SocketChannel skChannel = sskChannel.accept();//获取缓冲区ByteBuffer buffer = ByteBuffer.allocate(1024);//接收客户端数据并存入本地FileChannel outChannel = FileChannel.open(Paths.get("2.jpg"), StandardOpenOption.WRITE, StandardOpenOption.CREATE);while(skChannel.read(buffer) != -1){buffer.flip();outChannel.write(buffer);buffer.clear();}outChannel.close();skChannel.close();sskChannel.close();}

示例二

@Testpublic void client() throws IOException {//1 获取通道SocketChannel skChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1",9898));FileChannel fileChannel = FileChannel.open(Paths.get("1.jpg"), StandardOpenOption.READ);//2 获取缓冲区ByteBuffer buffer = ByteBuffer.allocate(1024);//3 通道传输出去数据while(fileChannel.read(buffer) != -1){buffer.flip();skChannel.write(buffer);buffer.clear();}System.out.println("发送图片完成");//阻塞式必须告诉服务端发送完成服务端才可以接收到数据skChannel.shutdownOutput();//接收服务端反馈int len = 0;while((len = skChannel.read(buffer)) != -1){buffer.flip();System.out.println(new String(buffer.array(),0,len));buffer.clear();}//4 关闭流fileChannel.close();skChannel.close();}@Testpublic void server() throws IOException {// 获取通道ServerSocketChannel sskChannel = ServerSocketChannel.open();// 绑定连接sskChannel.bind(new InetSocketAddress(9898));//获取客户端连接通道SocketChannel skChannel = sskChannel.accept();//获取缓冲区ByteBuffer buffer = ByteBuffer.allocate(1024);//接收客户端数据并存入本地FileChannel outChannel = FileChannel.open(Paths.get("2.jpg"), StandardOpenOption.WRITE, StandardOpenOption.CREATE);while(skChannel.read(buffer) != -1){buffer.flip();outChannel.write(buffer);buffer.clear();}System.out.println("接收图片完成");byte[] src = "获取客户端图片成功".getBytes();buffer.put(src);buffer.flip();skChannel.write(buffer);outChannel.close();skChannel.close();sskChannel.close();}

2 非阻塞io,服务端可以在选择器上监控已经准备就绪的事件处理,不用一直等待

示例一

 public static void main(String[] args) throws IOException {//1 获取通道SocketChannel skChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 9898));//切换 为非阻塞式skChannel.configureBlocking(false);// 2 获取缓冲区ByteBuffer buffer = ByteBuffer.allocate(1024);Scanner scan = new Scanner(System.in);while(scan.hasNext()) {String str = scan.next();System.out.println(str);//3 缓冲区存储数据buffer.put((new Date()+"\n"+str).getBytes());//4 通道传输数据buffer.flip();skChannel.write(buffer);buffer.clear();}// 关闭通道skChannel.close();}@Testpublic  void client() throws IOException {//1 获取通道SocketChannel skChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 9898));//切换 为非阻塞式skChannel.configureBlocking(false);// 2 获取缓冲区ByteBuffer buffer = ByteBuffer.allocate(1024);//3 缓冲区存储数据buffer.put(new Date().toString().getBytes());//4 通道传输数据buffer.flip();skChannel.write(buffer);buffer.clear();// 关闭通道skChannel.close();}@Testpublic void server() throws IOException {//获取通道ServerSocketChannel sskChannel = ServerSocketChannel.open();// 切换为非阻塞式sskChannel.configureBlocking(false);//绑定客户端连接sskChannel.bind(new InetSocketAddress(9898));//获取选择器Selector selector = Selector.open();// 将通道注册到选择器SelectionKey selectionKey = sskChannel.register(selector, SelectionKey.OP_ACCEPT);
//        System.out.println("tongdao 注册选择器");//轮询式的获取选择器上已经准备就绪的事件while(selector.select()>0){// 获取当前选择器中所有的选择键(已经准备就绪的事件)Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();while(iterator.hasNext()){//获取准备就绪的事件SelectionKey next = iterator.next();
//                System.out.println("获取选择器"+next.isReadable()+"||"+next.isAcceptable());if (next.isAcceptable()) {//接收事件//获取客户端连接的通道,并注册选择器SocketChannel sChannel = sskChannel.accept();//切换为非阻塞sChannel.configureBlocking(false);//注册到选择器sChannel.register(selector,SelectionKey.OP_READ);}else if(next.isReadable()){//读事件//获取选择器上(读状态就绪)状态的通道SocketChannel socketChannel = (SocketChannel) next.channel();ByteBuffer buffer = ByteBuffer.allocate(1024);int len = 0;while((len=socketChannel.read(buffer)) !=-1){buffer.flip();System.out.println(new String(buffer.array(),0,len));buffer.clear();}}}//需要将这个从选择器中取消掉,不然下次循环还会获取准备就绪的这个事件,重复获取了 Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();iterator.remove();}}

示例二

  @Testpublic void client() throws IOException {DatagramChannel dc = DatagramChannel.open();dc.configureBlocking(false);ByteBuffer buffer = ByteBuffer.allocate(1024);buffer.put("ceshi DatagramChannel".getBytes());buffer.flip();dc.send(buffer,new InetSocketAddress("127.0.0.1",9898));buffer.clear();dc.close();}@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> iterator = selector.selectedKeys().iterator();while (iterator.hasNext()) {SelectionKey next = iterator.next();//只需要监控读事件if (next.isReadable()) {ByteBuffer buffer = ByteBuffer.allocate(1024);dc.receive(buffer);buffer.flip();System.out.println(new String(buffer.array(), 0, buffer.limit()));buffer.clear();}iterator.remove();}}}

 

四、管道:

javanio两个线程之间单向的数据传输:管道Pipe有个Source和Sink通道,数据会被写入Sink通道,从Source读取

 @Testpublic void test() throws IOException {//获取管道Pipe open = Pipe.open();//将缓冲区的数据写入管道Pipe.SinkChannel sink = open.sink();Pipe.SourceChannel source = open.source();ByteBuffer buffer = ByteBuffer.allocate(1024);buffer.put("测试sink通道".getBytes());System.out.println(buffer.position());System.out.println(buffer.limit());buffer.flip();System.out.println(buffer.position());System.out.println(buffer.limit());sink.write(buffer);System.out.println(buffer.position());System.out.println(buffer.limit());// 读取缓冲区的数据//切换操作模式,将位置置为初始位置,才可以读取buffer.flip();System.out.println(buffer.position());System.out.println(buffer.limit());source.read(buffer);System.out.println(new String(buffer.array(),0,buffer.limit()));//关闭通道sink.close();source.close();}

 

  相关解决方案