博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【转】java NiO 学习笔记
阅读量:6417 次
发布时间:2019-06-23

本文共 6302 字,大约阅读时间需要 21 分钟。

hot3.png

1. Channel

NIO中常用的通道有四个:

  • FileChannel : 文件
  • DatagramChannel : UDP 读取网络数据
  • SocketChannel : TCP 读取网络数据
  • ServerSocketChannel : 监听新进来的TCP连接,像Web服务器那样。对每一个新进来的连接都会创建一个SocketChannel

Java NIO的通道类似流,但又有些不同:

  • 既可以从通道中读取数据,又可以写数据到通道。但流的读写通常是单向的。

  • 通道可以异步地读写。

  • 通道中的数据总是要先读到一个Buffer,或者总是要从一个Buffer中写入。

    http://ifeve.com/wp-content/uploads/2013/06/overview-channels-buffers.png

2. Buffer

和Channel进行交互,从Channel中读取数据,or将数据写入Channel

基本用法

当向buffer写入数据时,buffer会记录下写了多少数据。一旦要读取数据,需要通过flip()方法将Buffer从写模式切换到读模式。在读模式下,可以读取之前写入到buffer的所有数据

一旦读完了所有的数据,就需要清空缓冲区,让它可以再次被写入

读写数据的一般步骤

  • 写入数据到Buffer: ByteBuffer buf = ByteBuffer.allocate(48); int bytesRead =channel.read(buf)
  • 调用flip()方法: buf.flip()
  • 从Buffer中读取数据 : buf.get()
  • 清空缓存区: clear()方法会清空整个缓冲区。compact()方法只会清除已经读过的数据
RandomAccessFile aFile = new RandomAccessFile("data/nio-data.txt", "rw");FileChannel inChannel = aFile.getChannel();//create buffer with capacity of 48 bytesByteBuffer buf = ByteBuffer.allocate(48);int bytesRead = inChannel.read(buf); //read into buffer.while (bytesRead != -1) {  buf.flip();  //make buffer ready for read  while(buf.hasRemaining()){      System.out.print((char) buf.get()); // read 1 byte at a time  }  buf.clear(); //make buffer ready for writing  bytesRead = inChannel.read(buf);}aFile.close();

三个参数 capacity,position和limit

http://ifeve.com/wp-content/uploads/2013/06/buffers-modes.png

capacity

表示容量

position

表示当前读写的位置,读模式时,从该位置开始读数据,读完之后,移到下一个位置;写模式时,从该位置写入数据,写完之后,移到下一个可写的位置;当有写切换到读时,重置为0

limit

限制,读模式下,表示最多可以读多少数据;在写模式下,Buffer的limit表示你最多能往Buffer里写多少数据。 写模式下,limit等于Buffer的capacity

分配buffer

每一个Buffer类都有一个allocate方法

ByteBuffer buf = ByteBuffer.allocate(48);CharBuffer buf = CharBuffer.allocate(1024);

向Buffer中写数据

  1. 从Channel写到Buffer。

    int bytesRead = inChannel.read(buf);

  2. 通过Buffer的put()方法写到Buffer里。

    buf.put(127);

模式切换 flip()方法

flip方法将Buffer从写模式切换到读模式。调用flip()方法会将position设回0,并将limit设置成之前position的值

从Buffer中读取数据

  1. 从Buffer读取数据到Channel

    int bytesWritten = inChannel.write(buf);

  2. 使用get()方法从Buffer中读取数据。

    byte aByte = buf.get()

rewind()方法

Buffer.rewind()将position设回0,所以你可以重读Buffer中的所有数据。limit保持不变,仍然表示能从Buffer中读取多少个元素(byte、char等)

buf.rewind()

3.Scatter/Gather

scatter/gather用于描述从Channel中读取或者写入到Channel的操作

分散(scatter)从Channel中读取是指在读操作时将读取的数据写入多个buffer中。因此,Channel将从Channel中读取的数据“分散(scatter)”到多个Buffer中。

聚集(gather)写入Channel是指在写操作时将多个buffer的数据写入同一个Channel,因此,Channel 将多个Buffer中的数据“聚集(gather)”后发送到Channel。

Scattering Reads

从Channel中读取数据到Buffer

http://ifeve.com/wp-content/uploads/2013/06/scatter.png

使用方法,channel,读取数据到一个 Buffer数组中,填充满一个之后再填充下一个

ByteBuffer header = ByteBuffer.allocate(128);ByteBuffer body   = ByteBuffer.allocate(1024);ByteBuffer[] bufferArray = { header, body };channel.read(bufferArray);

Gathering Writes

将多个Buffer中的数据写入Channel

http://ifeve.com/wp-content/uploads/2013/06/gather.png

ByteBuffer header = ByteBuffer.allocate(128);ByteBuffer body   = ByteBuffer.allocate(1024);//write data into buffersByteBuffer[] bufferArray = { header, body };channel.write(bufferArray);

依次将数组中的数据写入Channel,一个写完再写如下一个(注意写入的是有效数据)

4. Selector

Selector(选择器)是Java NIO中能够检测一到多个NIO通道,并能够知晓通道是否为诸如读写事件做好准备的组件。这样,一个单独的线程可以管理多个channel,从而管理多个网络连接

创建

Selector selector = Selector.open();

通道注册

channel.configureBlocking(false);SelectionKey key = channel.register(selector, Selectionkey.OP_READ | SelectionKey.OP_WRITE);	```	与Selector一起使用时,Channel必须处于非阻塞模式下。这意味着不能将FileChannel与Selector一起使用,因为FileChannel不能切换到非阻塞模式。而套接字通道都可以register()方法的第二个参数。这是一个“interest集合”,意思是在通过Selector监听Channel时对什么事件感兴趣。可以监听四种不同类型的事件:- Connect : SelectionKey.OP_CONNECT- Accept : SelectionKey.OP_ACCEPT- Read : SelectionKey.OP_READ- Write : SelectionKey.OP_WRITE### SelectionKey通道注册之后,返回一个 `SelectionKey` 对象,其中包含一下属性:- interest集合- interest集合是你所选择的感兴趣的事件集合- 判断是否为Accept事件 : `boolean isInterestedInAccept  = (selectionKey.interestOps() & SelectionKey.OP_ACCEPT) == SelectionKey.OP_ACCEPT;`- ready集合- 通道已经准备就绪的操作的集合 `int readySet = selectionKey.readyOps()`- 检测channel中什么事件或操作已经就绪: `boolean state = selectionKey.isAcceptable();`- Channel- 获取Channel : `Channel  channel  = selectionKey.channel();`- Selector- 获取Selector : `Selector selector = selectionKey.selector();`- 附加的对象(可选)- 可以将一个对象或者更多信息附着到SelectionKey上,这样就能方便的识别某个给定的通道- 可以附加 与通道一起使用的Buffer,或是包含聚集数据的某个对象  selectionKey.attach(theObject);  Object attachedObj = selectionKey.attachment();### 通过Selector选择通道> 一旦向Selector注册了一或多个通道,就可以调用几个重载的select()方法。这些方法返回你所感兴趣的事件(如连接、接受、读或写)已经准备就绪的那些通道调用了select()方法,并且返回值表明有一个或更多个通道就绪了,然后可以通过调用selector的selectedKeys()方法,访问“已选择键集(selected key set)”中的就绪通道注意每次迭代末尾的keyIterator.remove()调用。Selector不会自己从已选择键集中移除SelectionKey实例。必须在处理完通道时自己移除。下次该通道变成就绪时,Selector会再次将其放入已选择键集中。```javaSelector selector = Selector.open();channel.configureBlocking(false);SelectionKey key = channel.register(selector, SelectionKey.OP_READ);while(true) {int readyChannels = selector.select();if(readyChannels == 0) continue;Set selectedKeys = selector.selectedKeys();Iterator keyIterator = selectedKeys.iterator();while(keyIterator.hasNext()) {  SelectionKey key = keyIterator.next();  if(key.isAcceptable()) {      // a connection was accepted by a ServerSocketChannel.  } else if (key.isConnectable()) {      // a connection was established with a remote server.  } else if (key.isReadable()) {      // a channel is ready for reading  } else if (key.isWritable()) {      // a channel is ready for writing  }  keyIterator.remove();}}

5. FileChannel

文件通道,用于读写文件,阻塞模式

打开FileChannel

需要通过使用一个InputStream、OutputStream或RandomAccessFile来获取一个FileChannel实例

通过 FileInputStream 获取 FileChannel 示意

FileInputStream fileInputStream = new FileInputStream(new File("/tmp/tags.log"));FileChannel fileChannel = (fileInputStream).getChannel();ByteBuffer buffer = ByteBuffer.allocate(128);int size = fileChannel.read(buffer);buffer.flip();while (buffer.hasRemaining()) {    System.out.print(buffer.getChar());}fileInputStream.close();

使用 RandomAccessFile 获取 FileChannel 示意

RandomAccessFile randomAccessFile = new RandomAccessFile("/tmp/tags.log", "rw");FileChannel fileChannel = randomAccessFile.getChannel();String newData = "New String to write to file..." + System.currentTimeMillis();ByteBuffer buf = ByteBuffer.allocate(100);buf.clear();buf.put(newData.getBytes());buf.flip();while(buf.hasRemaining()) {    fileChannel.write(buf);}// 强制写入文件fileChannel.force(true);ByteBuffer buffer = ByteBuffer.allocate(100);int size = fileChannel.read(buffer);buffer.flip();while (buffer.hasRemaining()) {    byte[] bytes = new byte[buffer.limit()];    buffer.get(bytes, 0, buffer.limit());    System.out.print(new String(bytes));}fileChannel.close();randomAccessFile.close();

转载于:https://my.oschina.net/u/566591/blog/904625

你可能感兴趣的文章
RealServer配置脚本
查看>>
九月份技术指标 华为交换机的简单配置
查看>>
python 写json格式字符串到文件
查看>>
分布式文件系统MogileFS
查看>>
电力线通信载波模块
查看>>
linux vim详解
查看>>
Java23种设计模式案例:策略模式(strategy)
查看>>
XML解析之DOM4J
查看>>
图解微服务架构演进
查看>>
SQL PATINDEX 详解
查看>>
一些常用的网络命令
查看>>
CSP -- 运营商内容劫持(广告)的终结者
查看>>
DIV+CSS命名规范有助于SEO
查看>>
js生成二维码
查看>>
C指针练习
查看>>
web项目buildPath与lib的区别
查看>>
php对redis的set(集合)操作
查看>>
我的友情链接
查看>>
ifconfig:command not found的解决方法
查看>>
js使用正则表达式判断手机和固话格式
查看>>