NIO理解

Posted ustc-anmin

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了NIO理解相关的知识,希望对你有一定的参考价值。

ByteBuffer Test:

技术图片
package java_guide;

import java.nio.ByteBuffer;

public class ByteBufferMethods {
    public static void main(String[] args) {
        //分配缓冲区(Allocating a Buffer)
        ByteBuffer buffer = ByteBuffer.allocate(33);
        System.out.println("--------Test reset----------");
        //clear()方法,position将被设回0,limit被设置成 capacity的值
        buffer.clear();
        // 设置这个缓冲区的位置
        buffer.position(5);
        //将此缓冲区的标记设置在其位置。没有  buffer.mark();这句话会报错
        buffer.mark();
        buffer.position(10);
        System.out.println("before reset:" + buffer);
        //将此缓冲区的位置重置为先前标记的位置。(buffer.position(5))
        buffer.reset();
        System.out.println("after reset:" + buffer);

        System.out.println("--------Test rewind--------");
        //position = 0;limit = capacity;mark = -1; 有点初始化的味道,但是并不影响底层byte数组的内容
        buffer.clear();
        buffer.position(10);
        //返回此缓冲区的限制
        buffer.limit(15);
        System.out.println("before rewind:" + buffer);
        //把position设为0,mark设为-1,不改变limit的值
        buffer.rewind();
        System.out.println("before rewind:" + buffer);

        System.out.println("--------Test compact--------");
        buffer.clear();
        buffer.put("abcd".getBytes());
        System.out.println("before compact:" + buffer);
        System.out.println(new String(buffer.array()));
        //翻转就是将一个处于存数据状态的缓冲区变为一个处于准备取数据的状态  limit变为之前的position
        buffer.flip();
        System.out.println("after flip:" + buffer);
        //get()方法:相对读,从position位置读取一个byte,并将position+1,为下次读写作准备
        System.out.println((char) buffer.get());
        System.out.println((char) buffer.get());
        System.out.println((char) buffer.get());
        System.out.println("after three gets:" + buffer);
        System.out.println("	" + new String(buffer.array()));
        //把从position到limit中的内容移到0到limit-position的区域内,position和limit的取值也分别变成limit-position、capacity。
        // 如果先将positon设置到limit,再compact,那么相当于clear()
        buffer.compact();
        System.out.println("after compact:" + buffer);
        System.out.println("	" + new String(buffer.array()));
        System.out.println("------Test get-------------");
        buffer = ByteBuffer.allocate(32);
        buffer.put((byte) ‘a‘).put((byte) ‘b‘).put((byte) ‘c‘).put((byte) ‘d‘).put((byte) ‘e‘).put((byte) ‘f‘);
        System.out.println("before flip()" + buffer);

        // 转换为读取模式
        buffer.flip();
        System.out.println("before get():" + buffer);
        System.out.println((char) buffer.get());
        System.out.println("after get():" + buffer);
        // get(index)不影响position的值
        System.out.println((char) buffer.get(2));
        System.out.println("after get(index):" + buffer);
        byte[] dst = new byte[10];
        buffer.get(dst, 0, 2);
        System.out.println("after get(dst, 0, 2):" + buffer);
        System.out.println("	 dst:" + new String(dst));
        System.out.println("buffer now is:" + buffer);
        System.out.println("	" + new String(buffer.array()));

        System.out.println("--------Test put-------");
        ByteBuffer bb = ByteBuffer.allocate(32);
        System.out.println("before put(byte):" + bb);
        System.out.println("after put(byte):" + bb.put((byte) ‘z‘));
        System.out.println("	" + bb.put(2, (byte) ‘c‘));
        // put(2,(byte) ‘c‘)不改变position的位置
        System.out.println("after put(2,(byte) ‘c‘):" + bb);
        System.out.println("	" + new String(bb.array()));
        // 这里的buffer是 abcdef[pos=3 lim=6 cap=32]
        bb.put(buffer);
        System.out.println("after put(buffer):" + bb);
        System.out.println("	" + new String(bb.array()));
    }

}
View Code

 

 输出:

技术图片
--------Test reset----------
before reset:java.nio.HeapByteBuffer[pos=10 lim=33 cap=33]
after reset:java.nio.HeapByteBuffer[pos=5 lim=33 cap=33]
--------Test rewind--------
before rewind:java.nio.HeapByteBuffer[pos=10 lim=15 cap=33]
before rewind:java.nio.HeapByteBuffer[pos=0 lim=15 cap=33]
--------Test compact--------
before compact:java.nio.HeapByteBuffer[pos=4 lim=33 cap=33]
abcd                             
after flip:java.nio.HeapByteBuffer[pos=0 lim=4 cap=33]
a
b
c
after three gets:java.nio.HeapByteBuffer[pos=3 lim=4 cap=33]
    abcd                             
after compact:java.nio.HeapByteBuffer[pos=1 lim=33 cap=33]
    dbcd                             
------Test get-------------
before flip()java.nio.HeapByteBuffer[pos=6 lim=32 cap=32]
before get():java.nio.HeapByteBuffer[pos=0 lim=6 cap=32]
a
after get():java.nio.HeapByteBuffer[pos=1 lim=6 cap=32]
c
after get(index):java.nio.HeapByteBuffer[pos=1 lim=6 cap=32]
after get(dst, 0, 2):java.nio.HeapByteBuffer[pos=3 lim=6 cap=32]
     dst:bc        
buffer now is:java.nio.HeapByteBuffer[pos=3 lim=6 cap=32]
    abcdef                          
--------Test put-------
before put(byte):java.nio.HeapByteBuffer[pos=0 lim=32 cap=32]
after put(byte):java.nio.HeapByteBuffer[pos=1 lim=32 cap=32]
    java.nio.HeapByteBuffer[pos=1 lim=32 cap=32]
after put(2,(byte) ‘c‘):java.nio.HeapByteBuffer[pos=1 lim=32 cap=32]
    z c                             
after put(buffer):java.nio.HeapByteBuffer[pos=4 lim=32 cap=32]
    zdef          
View Code

 

 

FileChannel Test:

技术图片
package java_guide;

import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

public class FileChannelTest {
    public static void main(String[] args) {
        RandomAccessFile raf;
        try {
            raf = new RandomAccessFile("D:\workspace\jian_zhi_offer\src\resource\test.txt", "rw");
            FileChannel inChannel = raf.getChannel();
            ByteBuffer buffer = ByteBuffer.allocate(48);
            int bytesRead = inChannel.read(buffer);

            ByteBuffer buffer2 = ByteBuffer.allocate(48);
            buffer2.put("hello world
".getBytes());
            buffer2.flip();
            inChannel.write(buffer2, 0);
            while (bytesRead != -1) {
                System.out.println("Reads:" + bytesRead);
                buffer.flip();
                while (buffer.hasRemaining()) {
                    System.out.print((char) buffer.get());
                }
                buffer.clear();
                bytesRead = inChannel.read(buffer);
            }
            raf.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
View Code

 

技术图片
Reads:48
hello world
ldvvv
ddddddddddddd
fffffffffffffReads:44
fhello world
eeeeeeeeeeeeeeeeeeee
54654465
View Code

 

通过上述实例代码,我们可以大概总结出FileChannel的一般使用规则:

1. 开启FileChannel

使用之前,FileChannel必须被打开 ,但是你无法直接打开FileChannel(FileChannel是抽象类)。需要通过 InputStreamOutputStreamRandomAccessFile 获取FileChannel。

2. 从FileChannel读取数据/写入数据

从FileChannel中读取数据/写入数据之前首先要创建一个Buffer(缓冲区)对象,

3. 关闭FileChannel


 

C/S架构的channel:

技术图片
package java_guide;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;

public class WebClient {
    public static void main(String[] args) throws IOException {
        SocketChannel socketChannel = SocketChannel.open();
        socketChannel.connect(new InetSocketAddress("127.0.0.1", 3333));
        ByteBuffer writeBuffer = ByteBuffer.allocate(128);
        writeBuffer.put("hello this message is from client".getBytes());
        writeBuffer.flip();
        socketChannel.write(writeBuffer);
        ByteBuffer readBuffer = ByteBuffer.allocate(128);
        socketChannel.read(readBuffer);
        StringBuffer stringBuffer = new StringBuffer();
        readBuffer.flip();
        while (readBuffer.hasRemaining()) {
            stringBuffer.append((char) readBuffer.get());
        }
        System.out.println("从服务端接受到的数据:"+stringBuffer);
        socketChannel.close();
    }
}
View Code

 

技术图片
package java_guide;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;

public class WebServer {
    public static void main(String[] args) throws IOException {
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.socket().bind(new InetSocketAddress("127.0.0.1",3333));
        SocketChannel socketChannel = serverSocketChannel.accept();
        ByteBuffer writeBuffer = ByteBuffer.allocate(128);
        writeBuffer.put("hello this is a message from server".getBytes());
        writeBuffer.flip();
        socketChannel.write(writeBuffer);
        ByteBuffer readBuffer = ByteBuffer.allocate(128);
        socketChannel.read(readBuffer);
        StringBuffer stringBuffer = new StringBuffer();
        readBuffer.flip();
        while(readBuffer.hasRemaining()){
            stringBuffer.append((char)readBuffer.get());
        }
        System.out.println("从客户端接收到的数据:"+stringBuffer);
        socketChannel.close();
        serverSocketChannel.close();
    }
}
View Code

 


 

 Selector(选择器)的使用方法介绍

1. Selector的创建

通过调用Selector.open()方法创建一个Selector对象,如下:

Selector selector = Selector.open();

 

2. 注册Channel到Selector

channel.configureBlocking(false);

SelectionKey key = channel.register(selector, Selectionkey.OP_READ);

 

 

register() 方法的第二个参数。这是一个“ interest集合 ”,意思是在通过Selector监听Channel时对什么事件感兴趣。可以监听四种不同类型的事件:

  • Connect

  • Accept

  • Read

  • Write

通道触发了一个事件意思是该事件已经就绪。比如某个Channel成功连接到另一个服务器称为“ 连接就绪 ”。一个Server Socket Channel准备好接收新进入的连接称为“ 接收就绪”。一个有数据可读的通道可以说是“ 读就绪 ”。等待写数据的通道可以说是“ 写就绪 ”。

这四种事件用SelectionKey的四个常量来表示:

SelectionKey.OP_CONNECT

SelectionKey.OP_ACCEPT

SelectionKey.OP_READ

SelectionKey.OP_WRITE

 

如果你对不止一种事件感兴趣,使用或运算符即可,如下:int interestSet = SelectionKey.OP_READ | SelectionKey.OP_WRITE;

3. SelectionKey介绍

一个SelectionKey键表示了一个特定的通道对象和一个特定的选择器对象之间的注册关系。

key.attachment(); //返回SelectionKey的attachment,attachment可以在注册channel的时候指定。
key.channel(); // 返回该SelectionKey对应的channel。
key.selector(); // 返回该SelectionKey对应的Selector。
key.interestOps(); //返回代表需要Selector监控的IO操作的bit mask
key.readyOps(); // 返回一个bit mask,代表在相应channel上可以进行的IO操作。

 

三 模板代码

一个服务端的模板代码:

技术图片
ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.socket().bind(new InetSocketAddress("localhost", 8080));
ssc.configureBlocking(false);
Selector selector = Selector.open();
ssc.register(selector, SelectionKey.OP_ACCEPT);
while(true) {
   int readyNum = selector.select();
   if (readyNum == 0) {
       continue;
   }

 

   Set<SelectionKey> selectedKeys = selector.selectedKeys();

   Iterator<SelectionKey> it = selectedKeys.iterator();

 

   while(it.hasNext()) {

       SelectionKey key = it.next();

 

       if(key.isAcceptable()) {

           // 接受连接

       } else if (key.isReadable()) {

           // 通道可读

       } else if (key.isWritable()) {

           // 通道可写

       }

       it.remove();

   }
}
View Code

 

 

 

参考:微信公众号

以上是关于NIO理解的主要内容,如果未能解决你的问题,请参考以下文章

Java NIO Reactor网络编程模型的深度理解

关于BIO,NIO,AIO的理解

金蝶handler中 collection 代码片段理解

调用模板化成员函数:帮助我理解另一个 *** 帖子中的代码片段

如何理解这段代码片段中的两对括号?

深入理解NIO