Contents
  1. 1. 总结
  2. 2. Channel(对缓冲区进行双向操作)
  3. 3. Buffer
    1. 3.1. Buffer的基本用法
    2. 3.2. 关键字:
  4. 4. Selector
  5. 5. SelectionKey
  6. 6. 文本复制的两种方式

Demo 源码:
https://github.com/huangzhenshi/IO_NIO_NIO2Dem

总结

  • NIO 分为NIO 和NIO2 ,NIO2是JDK1.7的新特性,NIO1是 1.4的新特性,做了操作简化
  • IO 和NIO最大的区别在于阻塞和非阻塞,这在socket编程聊天室时区别很大。IO每个连接都需要一个线程挂起去处理一次IO请求,一直到作业完成。而NIO则通过轮询的方式异步去处理每个channel。

参考blog:
http://ifeve.com/selectors/
https://www.cnblogs.com/wnlja/p/4368127.html

Channel(对缓冲区进行双向操作)

  • FileChannel (字节channel,只有阻塞的)
  • DatagramChannel (UDP 交互)
  • SocketChannel (TCP 交互)
  • ServerSocketChannel (TCP 服务端,监听新进来的TCP连接)

Buffer

缓冲区本质上是一块可以写入数据,然后可以从中读取数据的内存。这块内存被包装成NIO Buffer对象,并提供了一组方法,用来方便的访问该块内存

Buffer的基本用法

  1. 写入数据到Buffer,read(buf)
  2. 调用flip()方法
  3. 从Buffer中读取数据,buf.get()
  4. 调用clear()方法或者compact()方法,清空缓存区
public static void main(String[] args) throws IOException {
RandomAccessFile aFile = new RandomAccessFile(fileFrom, "rw");
FileChannel inChannel = aFile.getChannel();
//create buffer with capacity of 48 bytes
ByteBuffer buf = ByteBuffer.allocate(48);
//从channel中读取最多buf数组大小的内容缓冲池中
int bytesRead = inChannel.read(buf);
//如果读取的内容不为空
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();
}

关键字:

  1. capacity:缓冲池的大小
  2. position:初始值为0

  • 当你写数据到Buffer中时,position表示当前的位置。初始的position值为0.当一个byte、long等数据写到Buffer后, position会向前移动到下一个可插入数据的Buffer单元。position最大可为capacity –1

  • 当读取数据时,也是从某个特定位置读。当将Buffer从写模式切换到读模式,position会被重置为0. 当从Buffer的position处读取数据时,position向前移动到下一个可读的位置。
  1. limit:读模式下,表示之前写的position位置,写时就是capacity
  2. flip()方法:将Buffer从写模式切换到读模式。调用flip()方法会将position设回0,并将limit设置成之前position的值。

Selector

http://ifeve.com/selectors/
能够检测一到多个NIO通道,并能够知晓通道是否为诸如读写事件做好准备的组件,仅用单个线程来处理多个Channels的好处是,只需要更少的线程来处理通道

  • Selector selector = Selector.open();//创建
  • SelectionKey key = channel.register(selector,Selectionkey.OP_READ);
    第二个参数表示channel开放什么事件给selector:Connect、Accept、Read、Write
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();
}
}

SelectionKey

SelectionKey key =socketChannel.register(selector, SelectionKey.OP_READ, ByteBuffer.allocate(bufSize));

以上代码看出来,一个key管理映射到:

  • channel
  • selector
  • interest集合 (监听的事件类型)
  • ready集合 (监听已出发事件集合)
  • attachment (读写交互buffer).
int interestSet = selectionKey.interestOps();
boolean isInterestedInAccept = (interestSet & SelectionKey.OP_ACCEPT) == SelectionKey.OP_ACCEPT;
int readySet = selectionKey.readyOps();
boolean isInterestedInAccept =selectionKey.isAcceptable();
Channel channel = selectionKey.channel();
Selector selector = selectionKey.selector();
selectionKey.attach(theObject);
Object attachedObj = selectionKey.attachment();

文本复制的两种方式

  1. 利用函数transferFrom复制文本

    public static void NIOCopyFile() throws Exception{
    RandomAccessFile fromFile = new RandomAccessFile(fileFrom, "rw");
    FileChannel fromChannel = fromFile.getChannel();
    RandomAccessFile toFile = new RandomAccessFile(fileTo, "rw");
    FileChannel toChannel = toFile.getChannel();
    long position = 0;
    long count = fromChannel.size();
    toChannel.transferFrom(fromChannel, position, count);
    }
  2. 通过常规的Channel、Buffer进行文本的复制,2个Channel对一个ByteBuffer进行操作

    public static void copyFileNioOrigin() throws Exception{
    RandomAccessFile aFile = new RandomAccessFile(fileFrom, "rw");
    RandomAccessFile toFile = new RandomAccessFile(fileTo, "rw");
    FileChannel inChannel = aFile.getChannel();
    FileChannel toChannel = toFile.getChannel();
    ByteBuffer buf = ByteBuffer.allocate(1024);
    while (inChannel.read(buf) != -1) {
    buf.flip();
    toChannel.write(buf);
    buf.clear();
    }
    aFile.close();
    toFile.close();
    }
Contents
  1. 1. 总结
  2. 2. Channel(对缓冲区进行双向操作)
  3. 3. Buffer
    1. 3.1. Buffer的基本用法
    2. 3.2. 关键字:
  4. 4. Selector
  5. 5. SelectionKey
  6. 6. 文本复制的两种方式