NIO 代表New I/O,从名称上看,它代表了一组新的I/O 标准,而不是旧的基于流的I/O。从JDK1.4开始就包含在JDK中。
与旧的IO 流相比,NIO 是基于块的,以块的形式处理数据。最重要的组件是缓冲区和通道。缓冲区是一块连续的内存块,是NIO读写数据的载体。通道用于向缓冲区读取和写入数据,是访问缓冲区的接口。
Buffer的基本原理
缓冲区中最重要的三个参数:位置、容量和限制。这三个含义如下
位置:表示从读写数据后的位置开始的当前缓冲区位置。 capacity : 表示缓冲区的最大容量限制(limit) : 表示缓冲区的实际上限,始终小于或等于capacity 。 缓冲区容量保持不变。同时,位置(position)和上限(limit)可以根据实际需要进行改变。也就是说,通过改变当前位置和上限值,就可以操作缓冲区中任意位置的数据。
Buffer的常用方法
NIO 提供了一组用于操作缓冲区位置和上限以及向缓冲区读取和写入数据的方法。
put() //将数据添加到缓冲区位置。然后位置向后移动并且不能超过极限限制。 get() //读取当前位置的数据。然后位置向后移动并且不能超过极限限制。 flick() //设置当前位置的限制,然后将位置设置为0。 rewind() //仅将当前位置设置为0remaining //获取缓冲区中当前位置与上限之间的元素数量。 limit(有效元素数量) hasRemaining() //判断当前缓冲区是否有有效数量的元素mark() //标记当前位置reset() //将当前位置移动到下一个返回位置标记mark. Duplicate() //通过复制缓冲区来创建缓冲区
//创建一个容量为10 的缓冲区ByteBuffer byteBuffer1=ByteBuffer.allocate(10); //创建一个缓冲区来包装数据ByteBuffer2=ByteBuffer.wrap('abcde'.getBytes());
//创建容量为10的缓冲区ByteBuffer byteBuffer=ByteBuffer.allocate(10);System.out.println('Position:'+byteBuffer.position()); //0System.out.println('上限: ' +byteBuffer .limit()); //10System.out.println('Capacity: '+byteBuffer.capacity()); //10 向缓冲区添加数据
//创建容量为10的缓冲区ByteBuffer byteBuffer=ByteBuffer.allocate(10);//向缓冲区添加数据byteBuffer.put('abcde'.getBytes()); System.out.println('position: ' +byteBuffer .position()); //5System.out.println('限制上限: '+byteBuffer.limit()); //10System.out.println('容量容量: '+byteBuffer.capacity());/10 倒带缓冲区
倒带功能将位置设置为0 并清除标记。
//创建容量为10的缓冲区ByteBuffer byteBuffer=ByteBuffer.allocate(10);//向缓冲区添加数据byteBuffer.put('abcde'.getBytes()); System.out.println('position: ' +byteBuffer .position()); //5System.out.println('限制上限: '+byteBuffer.limit()); //10System.out.println('容量容量: '+byteBuffer.capacity());/10System.out.println('----------------');//重置缓冲区byteBuffer.rewind();System.out.println('position: '+ byteBuffer . position()); //0System.out.println('限制上限:'+byteBuffer.limit()); //10System.out.println('容量容量:'+byteBuffer.capacity()); 10 翻转复位缓冲器
Flip函数对位置设置限制,然后将位置设置为0并清除标记。
//创建容量为10的缓冲区ByteBuffer byteBuffer=ByteBuffer.allocate(10);//向缓冲区添加数据byteBuffer.put('abcde'.getBytes()); System.out.println('position: ' +byteBuffer .position()); //5System.out.println('限制上限: '+byteBuffer.limit()); //10System.out.println('容量容量: '+byteBuffer.capacity());/10System.out.println('----------------');//重置缓冲区byteBuffer.flip();System.out.println('position: '+ byteBuffer . position()); //0System.out.println('限制上限:'+byteBuffer.limit()); //5System.out.println('容量容量:'+byteBuffer.capacity()); 10 clear 清除缓冲区
clear方法还将位置设置为0,对容量大小设置限制,并清除标记。
//创建容量为10的缓冲区ByteBuffer byteBuffer=ByteBuffer.allocate(10); //设置上限为5byteBuffer.limit(5) //向缓冲区添加数据byteBuffer.put('abcde'.getBytes ; () );System.out.println('位置位置:'+byteBuffer.position()); //5System.out.println('限制上限:'+byteBuffer.limit()); +byteBuffer.capacity()); //10System.out.println('------------');//重置缓冲区byteBuffer.clear(); '+byteBuffer.position()); //0System.out.println('限制上限:'+byteBuffer.limit()); //10System.out.println('容量:'+byteBuffer.capacity() );//10 标记并恢复
ByteBufferbuffer=ByteBuffer.allocate(10);//向buffer添加数据buffer.put('abcde'.getBytes());//创建markbuffer.mark();System.out.println( 'Markposition :'+buffer .position()); //5//再向buffer添加5个字节的数据buffer.put('fijkl'.getBytes());System.out.println('当前位置: '+buffer.position() ); //10//恢复标记positionbuffer.reset();System.out.println('恢复标记位置:'+buffer.position());//5 010- 1010 FileChannel 用于文件操作的通道。可用于读取和写入文件。
//创建读取文件的通道FileChannel fisChannel=new FileInputStream('day05/src/a.txt').getChannel(); //创建写入文件的通道FileChannel fosChannel=new FileOutputStream('day05/src/b. txt ').getChannel();//创建缓冲区ByteBufferbuffer=ByteBuffer.allocate(2);while (fisChannel.read(buffer)!=-1){ System.out.println( 'position:'+buffer. position( )); //0 System.out.println('limit:'+buffer.limit()); //2 //重置缓冲区(准备输出缓冲区数据) fosChannel. write(buffer); buffer(准备输入缓冲区数据) buffer.clear();}//关闭通道fisChannel.close();fosChannel.close(); 以下代码为SocketChannel 使用通道上传。将文件发送到服务器
public class Client { public static void main(String[] args) throws IOException { //创建通道SocketChannel. allocate(1024); //读取本地文件数据到缓冲区FileChannel fisChannel=new FileInputStream('day05/src/a.txt').getChannel(); while (fisChannel.read(buffer)!=-1) . ); //准备写入数据socketChannel.write(buffer); //准备读取数据} //关闭本地通道fisChannel.shutdownOutput(); //由服务器回写读取数据。 buffer.clear(); int len=SocketChannel.read(buffer); //关闭套接字通道。
public class Server { public static void main(String[] args) throws IOException { //1. 创建一个ServerSocketChannel 通道。 serverSocketChannel=ServerSocketChannel.open() //2. 绑定端口号。 //3. 非阻塞serverSocketChannel.configureBlocking(false); System.out.println('Server is on'); //4. 获取客户端通道,如果存在则返回给客户端。客户端连接的终止通道,否则返回null。randomUUID( ); FileChannel fosChannel=new FileOutputStream('day05/src/'+uuid+'.txt').getChannel(); ByteBuffer buffer=ByteBuffer.allocate(1024); //缓冲数据准备输出fosChannel.write(buffer); //准备将数据读入缓冲区} fosChannel.close(); resultBuffer=ByteBuffer.wrap('文件上传成功'.getBytes()) ;SocketChannel.write(resultBuffer);关闭客户端通道socketChannel.close();
FileChannel通道
选择器通常称为选择器。当然也可以翻译为多路复用器。这是Java NIO的核心组件之一,用于检查一个或多个NIO通道的可读可写状态。这样您就可以使用单个线程管理多个通道。这意味着您可以管理多个网络链接。
使用选择器的服务器模板代码
有了模板代码,当你编写程序时,几乎都会在模板代码中添加相应的业务代码。
ServerSocketChannel ssc=ServerSocketChannel.open();ssc.socket().bind(new InetSocketAddress('localhost', 8080));ssc.configureBlocking(false);Selector 选择器=Selector.open();ssc.register(selector, SelectionKey.OP_ACCEPT);while(true) { int readyNum=选择器.select(); if (readyNum==0) { 继续; } SetSelectionKey selectedKeys=选择器.selectedKeys() it.hasNext()) { SelectionKey key=it .next(); if(key.isAcceptable()) { //接受连接} else if (key.isReadable()) { //通道可读} else if (key .isWritable()) { //通道可写} it.remove(); }}
SocketChannel通道
根据上面的模板代码重写接收文件的服务器。
public class Server { public static void main(String[] args) { try { ServerSocketChannel ssc=ServerSocketChannel.open(); ssc.socket().bind(new InetSocketAddress('localhost', 10000)); Selector.open(); ssc.register(selector, SelectionKey.OP_ACCEPT); if (readyNum==0) { continue; } SetSelectionKey selectedKeys=selectedKeys(); while (it.hasNext); ()) {SelectionKey key=it.next() null; if (key.isAcceptable()) { System.out.println('isAcceptable'); //并声明仅此通道感兴趣。读取操作正在进行中。套接字通道1=ssc.accept(); 套接字通道1.configureBlocking(false); 套接字通道1.register(selector, UUID.randomUUID(); fosChannel('day05/src/'+uuid+') txt'); (); } else if (key.isReadable()) { System.out.println('isReadable') //通道可读的套接字Channel2=(SocketChannel) key.channel();用于将数据写入文件ByteBuffer readBuff=ByteBuffer.allocate(1024); while (socketChannel2.read(readBuff)0){ readBuff.flip(); //准备输出缓冲区数据fosChannel.write(readBuff) ; //准备将数据读入buffer } fosChannel.close(); key.interestOps(SelectionKey.OP_WRITE) } else if (key.isWritable()) { System.out.println(' isWritable'); //Channel 可写ByteBuffer writeBuff=ByteBuffer.allocate( 1024);write(writeBuff); key.interestOps(SelectionKey.OP_READ); } it.remove() } } } catch (Exception e) { //e.printStackTrace () };
版权声明:本文转载于今日头条,版权归作者所有。如有侵权,请联系本站编辑删除。