程序员潇然 发表于 2022-10-27 16:43:45

NIO编程模型以及核心组件简介 多路复用示例(五)

!(data/attachment/forum/202210/27/161654ybj4s6siz4eea6s3.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "image.png")

不管是多Reactor还是多线程模式,只是在基本Reactor模式下的扩展,基本原型是不变的。

NIO的编程模型,也是类似如此。

### 示例

引入多路复用的NIO示例程序。

```java
https://gitee.com/crazybytex/java-code-fragment/blob/master/src/main/java/com/crazybytex/fragment/nio/NioServer.java
```

```java
package com.crazybytex.fragment.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

/**
* @Author 本文作者 程序员潇然 疯狂的字节X https://www.crazybytex.com/
* @Date 2022/10/27 16:18
* @Description
**/
public class NioServer {
    public static void main(String[] args) throws IOException, InterruptedException {

      // 创建NIO ServerSocketChannel
      ServerSocketChannel serverSocket = ServerSocketChannel.open();
      serverSocket.socket().bind(new InetSocketAddress(9000));
      // 设置ServerSocketChannel为非阻塞
      serverSocket.configureBlocking(false);
      // 打开Selector处理Channel,即创建epoll
      Selector selector = Selector.open();
      // 把ServerSocketChannel注册到selector上,并且selector对客户端accept连接操作感兴趣
      serverSocket.register(selector, SelectionKey.OP_ACCEPT);
      System.out.println("服务启动成功");

      while (true) {
            // 阻塞等待需要处理的事件发生
            selector.select();

            // 获取selector中注册的全部事件的 SelectionKey 实例
            Set<SelectionKey> selectionKeys = selector.selectedKeys();
            Iterator<SelectionKey> iterator = selectionKeys.iterator();

            // 遍历SelectionKey对事件进行处理
            while (iterator.hasNext()) {
                SelectionKey key = iterator.next();
                // 如果是OP_ACCEPT事件,则进行连接获取和事件注册
                if (key.isAcceptable()) {
                  ServerSocketChannel server = (ServerSocketChannel) key.channel();
                  SocketChannel socketChannel = server.accept();
                  socketChannel.configureBlocking(false);
                  // 这里只注册了读事件,如果需要给客户端发送数据可以注册写事件
                  socketChannel.register(selector, SelectionKey.OP_READ);
                  System.out.println("客户端连接成功");

                  // 如果是OP_READ事件,则进行读取和打印
                } else if (key.isReadable()) {
                  SocketChannel socketChannel = (SocketChannel) key.channel();
                  ByteBuffer byteBuffer = ByteBuffer.allocate(128);
                  int len = socketChannel.read(byteBuffer);
                  // 如果有数据,把数据打印出来
                  if (len > 0) {
                        System.out.println("接收到消息:" + new String(byteBuffer.array()));

                        // 如果客户端断开连接,关闭Socket
                  } else if (len == -1) {
                        System.out.println("客户端断开连接");
                        socketChannel.close();
                  }
                }
                //从事件集合里删除本次处理的key,防止下次select重复处理
                iterator.remove();
            }
      }
    }
}

```

### 流程

!(data/attachment/forum/202210/27/170518xa67xylgcjjcz699.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "image.png")

### 主要概念对比

!(data/attachment/forum/202210/27/171409sf3nsx2xktn2l3xt.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "image.png")

### NIO模型与交互

java的NIO可以认为是Reactor模式的一个实现,提供了一个编程模型

!(data/attachment/forum/202210/27/173434vatrhuqcb9ktr4mu.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "image.png")

### NIO核心组件

!(data/attachment/forum/202210/27/200041ra1c6xzap6sskm5m.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "image.png")

NIO 有三大核心组件: Channel(通道), Buffer(缓冲区),Selector(多路复用器)
1、channel 类似于流,每个 channel 对应一个 buffer缓冲区,buffer 底层就是个数组
2、channel 会注册到 selector 上,由 selector 根据 channel 读写等事件的发生将其交由某个空闲的线程处理
3、NIO 的 Buffer 和 channel 都是既可以读也可以写

参照上面的流程,客户端通过buffer与channel交互,Selector通过事件机制与channel进行交互,进而达到客户端与Server的交互通信。

网络通信的编程接口仍旧是Socket,他的实现基础在NIO里面是Channel


!(data/attachment/forum/202210/31/161324i15jz77vzj1qoaat.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "image.png")


Channel用于源和目的地的连接,是一个双向通道。

而且,必须经过Buffer,不能直接对Channel进行数据的读写,可以查看类图层次,以及对应的read或者write方法的参数得到证明。

### 小结

NIO就是Java 对Reactor模式的实现,提供了 Buffer、Channel、Selector等核心组件。

通过Channel进行连接操作管理,通过Buffer进行数据读写,通过Selector进行事件绑定回调等。


!(data/attachment/forum/202206/16/141330jha7st9soow8772i.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "common_log.png")
`转载务必注明出处:程序员潇然,疯狂的字节X,https://crazybytex.com/thread-206-1-1.html `
页: [1]
查看完整版本: NIO编程模型以及核心组件简介 多路复用示例(五)