关于java:JAVA-NIO

36次阅读

共计 3575 个字符,预计需要花费 9 分钟才能阅读完成。

java 的网络编程中,会波及到 2 种 IO 模型,一个就是 BIO,即阻塞 IO。一个是 NIO,即非阻塞 IO。在默认的状况下,IO 是阻塞 IO,比方 tomcat 容器就是就是 BIO。而 redis, netty 等反对高并发服务,都是基于 NIO。NIO 和 BIO 有 3 点区别:1:BIO 是基于流 (stream) 的,比方:FileStream, SockectStream。而 NIO 是基于缓冲区 buffer 的,比方 LongBuffer, ByteBuffer。2:BIO 是阻塞的,NIO 是非阻塞的。3:NIO 有选择器(Selector),而 BIO 没有。NIO 有三大组件:Channel: 通道,Buffer:缓冲区,Selector:选择器。Channel:介绍:在 NIO 中,应用 Channel 进行通信,能够向 Channel 写入音讯,也能够从 Channel 读取音讯。Channel 能够分为如下类型:ServerSocketChannel:服务端的网络 Channel。能够绑定监听端口,能够承受客户端的网络连接。SocketChannel:示意网络连接 Channel,能够从该 Channel 读取数据,也能够向 Channel 写入数据
DatagramChannel:能通过 UDP 读写网络中的数据
FileChannel 从文件中读写数据

Buffer 介绍:在 NIO 中,从 channel 读出数据或者向 channel 写入数据,都是通过 buffer 操作,这样能够提高效率。依照类型不同,Buffer 也分为不同类型:比方:ByteBuffer, LongBuffer, IntBuffer, CharBuffer, FloatBuffer 等。应用 Buffer 读写数据个别遵循以下四个步骤:1. 写入数据到 Buffer
2. 调用 flip() 办法
3. 从 Buffer 中读取数据
4. 调用 clear()办法或者 compact()办法

当向 buffer 写入数据时,buffer 会记录下写了多少数据。一旦要读取数据,须要通过 flip()办法将 Buffer 从写模式切换到读模式。在读模式下,能够读取之前写入到 buffer 的所有数据。一旦读完了所有的数据,就须要清空缓冲区,让它能够再次被写入。有两种形式能清空缓冲区:调用 clear()或 compact()办法。clear()办法会清空整个缓冲区。compact()办法只会革除曾经读过的数据。任何未读的数据都被移到缓冲区的起始处,新写入的数据将放到缓冲区未读数据的前面。Buffer 有三个重要属性:capacity,position,limit。position 和 limit 的含意取决于 Buffer 处在读模式还是写模式。不论 Buffer 处在什么模式,capacity 的含意总是一样的。![image](/img/bVI7kq)

capacity: 作为一个内存块,Buffer 有一个固定的大小值,也叫“capacity”. 你只能往里写 capacity 个 byte、long,char 等类型。一旦 Buffer 满了,须要将其清空(通过读数据或者革除数据)能力持续写数据往里写数据。position: 当你写数据到 Buffer 中时,position 示意以后的地位。初始的 position 值为 0. 当一个 byte、long 等数据写到 Buffer 后,position 会向前挪动到下一个可插入数据的 Buffer 单元。position 最大可为 capacity – 1。当读取数据时,也是从某个特定地位读。当将 Buffer 从写模式切换到读模式,position 会被重置为 0。当从 Buffer 的 position 处读取数据时,position 向前挪动到下一个可读的地位。limit:在写模式下,Buffer 的 limit 示意你最多能往 Buffer 里写多少数据。写模式下,limit 等于 Buffer 的 capacity。当切换 Buffer 到读模式时,limit 示意你最多能读到多少数据。因而,当切换 Buffer 到读模式时,limit 会被设置成写模式下的 position 值。换句话说,你能读到之前写入的所有数据(limit 被设置成已写数据的数量,这个值在写模式下就是 position)。Selector 介绍:Selector(选择器)是 Java NIO 中可能检测一到多个 NIO 通道,并可能通晓通道是否为诸如读写事件做好筹备的组件。这样,一个独自的线程能够治理多个 channel,从而治理多个网络连接。Selector 反对的 Channel 的事件类型有:SelectionKey.OP_CONNECT, SelectionKey.OP_ACCEPT, SelectionKey.OP_READ, SelectionKey.OP_WRITE。服务端示例代码:
public void startNioServer() throws Exception {ServerSocketChannel serverChannel = ServerSocketChannel.open(); // 关上通道
 serverChannel.configureBlocking(false); // 非阻塞
 ServerSocket serverSocket = serverChannel.socket();
 serverSocket.bind(new InetSocketAddress(8090)); // 绑定端口
 System.out.println("bind port: 8090");
 Selector selector = Selector.open(); // 选择器
 serverChannel.register(selector, SelectionKey.OP_ACCEPT); // 注册到选择器
 while (selector.select() > 0) { // 判断事件筹备好的通道数量
 Iterator<SelectionKey> it = selector.selectedKeys().iterator();
 while (it.hasNext()) {SelectionKey key = it.next();
 if (key.isAcceptable()) { // 客户端连贯的事件
 ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel();
 SocketChannel socketChannel = serverSocketChannel.accept(); // 新建设的客户端连贯
 socketChannel.configureBlocking(false); // 设置为非阻塞
 socketChannel.register(selector, SelectionKey.OP_READ); // 注册可读事件
 }
            if (key.isReadable()) {SocketChannel socketChannel = (SocketChannel) key.channel();
 ByteBuffer buffer = ByteBuffer.allocate(1024); // 调配缓冲区,在堆内存调配
 int len = 0;
 while ((len = socketChannel.read(buffer)) > 0) {buffer.flip();
 System.out.println("NIO SERVER RECEIVE:" + new String(buffer.array(), 0, len));
 buffer.clear();}
                socketChannel.close();}
            it.remove();}
    }
    serverChannel.close();}

客户端示例代码:

public void startNioClient(int idx) throws Exception {SocketChannel socketChannel = SocketChannel.open(); // 关上通道
 socketChannel.configureBlocking(false); // 非阻塞
 socketChannel.connect(new InetSocketAddress(8090)); // 连贯近程服务
 while (!socketChannel.finishConnect()) {// 期待连贯实现}
    String msg = "client-" + idx + "send message.";
 ByteBuffer buffer = ByteBuffer.allocate(1024); // 调配缓冲区,默认是在堆上调配
 buffer.put(msg.getBytes()); // 写入数据
 buffer.flip(); // 反转为读模式
 socketChannel.write(buffer); // 向通道写入信息
 socketChannel.close();}

正文完
 0