一、Java NIO概述
- Java NIO由如下三个核心部分组成:
- Channels 管道;负责连接TCP、UDP、File等进行数据的读写;管道每次读写数据都需要经过Buffers
- Buffers 缓冲区;负责接收管道读取的数据/向管道传输数据;
- Selectors 选择器;负责注册的管道,并监听管道的数据流动;调用select方法,会阻塞到管道中事件发生,返回后可以对这个事件进行处理
二、Channel
Java NIO的Channel与流类似,区别:流的读写是单向的,而管道的是双工的,既可读又可写,不过在读写切换之前需要调用channel.flip()方法进行读写模式的切换。
而且 管道可以异步的读写。Channel的重要实现:
- FileChannel:文件读写
- DatagramChannle:UDP网络读写
- SocketChannel:TCP网络读写
- ServerSocketChannel:可以向Web服务器一样监听新进来的TCP连接,然后对于每个连接创建响应的SocketChannel读取其中的数据=
Channel通过Buffer进行数据读写的代码示例:
RandomAccessFile aFile = new RandomAccessFile("data/nio-data.txt", "rw");//定义一个文件 FileChannel inChannel = aFile.getChannel();//获取文件的管道 ByteBuffer buf = ByteBuffer.allocate(48);//创建一个每次可以读写48个字节的缓冲区 int bytesRead = inChannel.read(buf); //管道读取数据到缓冲区,每次读满48个字节介绍并返回读取的字节个数 while (bytesRead != -1) { //当数据读取完以后返回-1 System.out.println("Read " + bytesRead); buf.flip();//反转缓冲区,从写入模式切换到读取模式 while(buf.hasRemaining()){//读取缓冲区里的数据 System.out.print((char) buf.get()); } buf.clear();//缓冲区的数据满后,要clear()/compact()才能继续写入数据 bytesRead = inChannel.read(buf); } aFile.close();
- Buffer 本质上是一块儿可以写入数据,又可以从中读取数据的内存,被包装成了NIO Buffer对象,并提供了一组方法来访问该块内存。
- capacity:缓冲区的容量
- position:位置指示器,初始值都为零。写模式时,从零移动到capacity-1(或limit,即可用空间的限制)的地方,每写入一个值就会,position都会移动到下一个可写入位置的序号;从写模式切换到读模式时,position置零,然后开始移动,直到到上次写入的最后序号limit(即切换时,limit=position)
- limit:可读/写的限制
Buffer的实现类
- ByteBuffer
- MappedByteBuffer
- CharBuffer
- DoubleBufferoatBuffer
- IntBuffer
- LongBuffer
- ShortBuffer
常用方法
- allocate(int capacity):初始化一个缓冲区,指定其大小
- put()
三、Scatter和Gather
分散(scatter)从Channel中读取是指在读操作时将读取的数据写入多个buffer中。因此,Channel将从Channel中读取的数据“分散(scatter)”到多个Buffer中。
ByteBuffer header = ByteBuffer.allocate(128);
ByteBuffer body = ByteBuffer.allocate(1024);
ByteBuffer[] bufferArray = { header, body };
channel.read(bufferArray);
聚集(gather)写入Channel是指在写操作时将多个buffer的数据写入同一个Channel,因此,Channel 将多个Buffer中的数据“聚集(gather)”后发送到Channel。
ByteBuffer header = ByteBuffer.allocate(128);
ByteBuffer body = ByteBuffer.allocate(1024);
//write data into buffers
ByteBuffer[] bufferArray = { header, body };
channel.write(bufferArray);
注意buffer首先被插入到数组,然后再将数组作为channel.read() 的输入参数。read()方法按照buffer在数组中的顺序将从channel中读取的数据写入到buffer,当一个buffer被写满后,channel紧接着向另一个buffer中写。所以如果需要指定数据到指定的Channel中,必须使用数据的长度来定义相应Channel的容积