一、原理图

NIO 非阻塞 网络编程相关的(Selector、SelectionKey、ServerScoketChannel 和 SocketChannel) 关系梳理图:
image.png

说明:

  • 当客户端连接时,会通过 ServerSocketChannel 得到 SocketChannel
  • Selector 进行监听 select 方法, 返回有事件发生的通道的个数
  • 将 serverSocketChannel 注册到Selector上,register(selector, SelectionKey),一个 selector 上可以注册多个 serverSocketChannel
  • 注册后返回一个 SelectionKey, 会和该 Selector 关联(集合)
  • 监听 SelectionKey 进一步得到各个 SelectionKey (有事件发生)
  • 通过 SelectionKey 反向获取 SocketChannel , 方法 channel()
  • 可以通过得到的 channel , 完成业务处理

代码实例:
https://github.com/Wells-Lee/netty-demo/tree/master/src/main/java/com/wells/demo/nio/non/blocking

二、类说明

2.1、SelectionKey

SelectionKey,表示 Selector 和网络通道的注册关系, 共四种:

  • int OP_ACCEPT:有新的网络连接可以 accept,值为 16
  • int OP_CONNECT:代表连接已经建立,值为 8
  • int OP_READ:代表读操作,值为 1
  • int OP_WRITE:代表写操作,值为 4

源码中:
public static final int OP_READ = 1 << 0;
public static final int OP_WRITE = 1 << 2;
public static final int OP_CONNECT = 1 << 3;
public static final int OP_ACCEPT = 1 << 4;

SelectionKey相关方法:

  1. public abstract Selector selector(); // 得到与之关联的 Selector 对象
  2. public abstract SelectableChannel channel(); // 得到与之关联的 channel
  3. public abstract SelectionKey interestOps(int ops); // 设置或者改变监听事件
  4. public final boolean isReadable(); // 是否为读事件
  5. public final boolean isWritable(); // 是否为写事件
  6. public final boolean isConnectable(); // 是否为连接事件
  7. public final boolean isAcceptable(); // 是否为接受事件
  8. public final Object attachment(); // 得到与之关联的共享数据

2.2、ServerSocketChannel

ServerSocketChannel 在服务器端监听客户端 Socket 的连接;
相关方法:

  1. public static ServerSocketChannel open(); // 得到一个 ServerSocketChannel 连接
  2. public final ServerSocketChannel bind(SocketAddress local); // 设置服务器ip、端口
  3. public abstract SocketChannel accept(); // 接收一个客户端连接
  4. public final SelectionKey register(Selector sel, int ops); // 注册一个选择器并选择监听事件
  5. public final SelectableChannel configureBlocking(boolean block); // 设置是否阻塞

2.3、SocketChannel

SocketChannel,网络 IO 通道,具体负责进行读写操作。NIO 把缓冲区的数据写入通道,或者把通道里的数据读到缓冲区。
相关方法:

  1. public static SocketChannel open(SocketAddress remote); // 得到一个 SocketChannel 连接
  2. public final SelectableChannel configureBlocking(boolean block); // 社渚是否阻塞
  3. public abstract boolean isConnected(); // 连接服务器
  4. public abstract boolean finishConnect(); // 如果上面的方法连接失败,接下来就要通过该方法连接
  5. public final SelectionKey register(Selector sel, int ops); // 注册一个选择器,并设置监听事件
  6. public abstract int write(ByteBuffer src); // 写数据
  7. public final long read(ByteBuffer[] dsts); // 读数据
  8. public final void close(); // 关闭连接

三、实例练习

实例要求:

  • 编写一个 NIO 群聊系统,实现服务器端和客户端之间的数据简单通讯(非阻塞)
  • 实现多人群聊
  • 服务器端:可以监测用户上线,离线,并实现消息转发功能

  • 客户端:通过channel可以无阻塞发送消息给其它所有用户,同时可以接受其它用户发送的消息(有服务器转发得到)

代码实例:
https://github.com/Wells-Lee/netty-demo/tree/master/src/main/java/com/wells/demo/nio/groupchat

  1. <br />
  2. <br />
  3. <br />
  4. <br />
  5. <br />