文档章节

【转】java NiO 学习笔记

小灰灰Blog
 小灰灰Blog
发布于 2017/05/19 18:20
字数 1716
阅读 31
收藏 0

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 bytes
ByteBuffer 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 buffers

ByteBuffer[] 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会再次将其放入已选择键集中。

```java
Selector 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();

本文转载自:http://ifeve.com/overview/

共有 人打赏支持
上一篇: Shell学习笔记
下一篇: Java之写文件
小灰灰Blog
粉丝 183
博文 187
码字总数 326577
作品 0
武汉
程序员
私信 提问
Java NIO ByteBuffer学习

深入理解Apache Mina (6)---- Java Nio ByteBuffer与Mina ByteBuffer的区别 http://chinaestone.iteye.com/blog/468138 DirectBuffer及内存泄漏 http://blog.csdn.net/zhouhl_cn/article/d......

mj4738
2012/10/15
0
0
JAVA NIO编程入门(一)

JAVA NIO编程入门(一) 一、前言 笔者之前接触的NIO编程比较少,所以对这一块的基础也比较弱,NIO作为java编程中一个重要的模块,不能很好的掌握它,感觉自己在java方面就掌握的不够,所以,...

木木匠
09/01
0
0
一份关于 Java、Kotlin 与 Android 的学习笔记

JavaKotlinAndroidLearn 这是一份关于 Java 、Kotlin 、Android 的学习笔记,既包含对基础知识点的介绍,也包含对一些重要知识点的源码解析,笔记的大纲如下所示: Java 重拾Java(0)-基础知...

叶应是叶
08/08
0
0
动态生成字节码--Javassist

前言 Javaassist是一个高层的Java字节码处理类库,能运行时动态生成类,修改类。Javaassit能动态生成类的基础源于JavaClass的字节码技术:只要遵从规范,JavaClass可以来自任何地方。类似的技术还...

淡淡的倔强
08/15
0
0
最近仔细研究了一下Java的NIO以及线程并发,搞清了点思路,特作笔记如下(NIO篇)

[转]http://www.cnblogs.com/feidao/archive/2005/07/15/193788.html 因为前段时间的项目需要写一些高性能服务器,结果写出来的结果是五花八门,我们要求使用NIO编写异步服务器,但是竟然有人...

风林火山
2010/12/26
0
1

没有更多内容

加载失败,请刷新页面

加载更多

CentOS配置Tomcat监听80端口,虚拟主机

Tomcat更改默认端口为80 更改的配置文件是: /usr/local/tomcat/conf/server.xml [root@test-a ~]# vim /usr/local/tomcat/conf/server.xml # 找到 Connector port="8080" protocol="HTTP/1......

野雪球
今天
5
0
《稻盛和夫经营学》读后感心得体会3180字范文

《稻盛和夫经营学》读后感心得体会3180字范文: 一代日本经营之圣稻盛和夫凭借刻苦勤奋的精神以及深植于佛教的商业道德准则,成为了“佛系”企业家的代表人物。在《稻盛和夫经营学》“领导人...

原创小博客
今天
3
0
java框架学习日志-5(常见的依赖注入)

依赖注入(dependency injection) 之前提到控制反转(Inversion of Control)也叫依赖注入,它们其实是一个东西,只是看的角度不同,这章详细说一下依赖注入。 依赖——指bean对象创建依赖于...

白话
今天
4
0
红外接收器驱动开发

背景:使用系统的红外遥控软件没有反应,然后以为自己接线错误,反复测试,结果烧坏了一个红外接收器,信号主板没有问题。所以自己开发了一个红外接收器的python驱动。接线参见https://my.os...

mbzhong
今天
2
0
ActiveMQ消息传送机制以及ACK机制详解

AcitveMQ是作为一种消息存储和分发组件,涉及到client与broker端数据交互的方方面面,它不仅要担保消息的存储安全性,还要提供额外的手段来确保消息的分发是可靠的。 一. ActiveMQ消息传送机...

watermelon11
今天
2
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部