SelectionKey的作用:
就是Selector和Channel之间的桥梁。即Selector和SelectionKey 的事件产生关系
selector 对SelectionKey 1对n
SelectionKey 对Channel:1对1
eg:
1> Selector.select();就Selector获取已经在该Selector注册的SelectionKey事件。没有事件会一直阻塞。
2> 然后通过Selector.selectedKeys()获取所有的SelectionKey
3>SelectionKey和Channel是一对一的关系。通过SelectionKey + 其事件:获得到Channel对象进而进行读取客户端的发送数据或服务端给客户端回响应
源码:
package java.nio.channels;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
public abstract class SelectionKey {
public abstract SelectableChannel channel();
public abstract Selector selector();
public abstract boolean isValid();
public abstract void cancel();
public abstract int interestOps();
public abstract SelectionKey interestOps(int ops);
public abstract int readyOps();
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;
public final boolean isReadable() { return (readyOps() & OP_READ) != 0;}
public final boolean isWritable() {return (readyOps() & OP_WRITE) != 0;}
public final boolean isConnectable() {return (readyOps() & OP_CONNECT) != 0;}
public final boolean isAcceptable() {return (readyOps() & OP_ACCEPT) != 0;}
private volatile Object attachment = null;
private static final AtomicReferenceFieldUpdater
attachmentUpdater = AtomicReferenceFieldUpdater.newUpdater(
SelectionKey.class, Object.class, "attachment"
);
public final Object attach(Object ob) {return attachmentUpdater.getAndSet(this, ob);}
public final Object attachment() {return attachment;}
}
主要的实现类:
package sun.nio.ch; import java.nio.channels.CancelledKeyException; import java.nio.channels.SelectableChannel; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.spi.AbstractSelectionKey; public class SelectionKeyImpl extends AbstractSelectionKey { final SelChImpl channel; public final SelectorImpl selector; private int index; private volatile int interestOps; private int readyOps; SelectionKeyImpl(SelChImpl var1, SelectorImpl var2) { this.channel = var1;this.selector = var2;} public SelectableChannel channel() {return (SelectableChannel)this.channel;} public Selector selector() {return this.selector;} int getIndex() {return this.index;} void setIndex(int var1) {this.index = var1;} private void ensureValid() { if (!this.isValid()) {throw new CancelledKeyException();} } public int interestOps() {this.ensureValid();return this.interestOps;} public SelectionKey interestOps(int var1) {this.ensureValid();return this.nioInterestOps(var1);} public int readyOps() {this.ensureValid(); return this.readyOps;} public void nioReadyOps(int var1) {this.readyOps = var1;} public int nioReadyOps() {return this.readyOps;} public SelectionKey nioInterestOps(int var1) { if ((var1 & ~this.channel().validOps()) != 0) { throw new IllegalArgumentException(); } else { this.channel.translateAndSetInterestOps(var1, this); this.interestOps = var1; return this; } } public int nioInterestOps() {return this.interestOps;} }
通过源码可以看到有5个点:
1> Interest Set兴趣集合
2> Ready Set就绪集合
3> Channel通道
4> Selector选择器
5> Attach附加对象
对应Selector监听管道的4个事件
这四种事件用SelectionKey的四个常量来表示:
SelectionKey.OP_CONNECT 一个channel成功连接到服务器称为”连接就绪“
SelectionKey.OP_ACCEPT 一个server socket channel服务准备好接收新进入的client连接称为”接收就绪“
SelectionKey.OP_READ 一个有数据可读的通道可以说是”读就绪“
OP_READ 事件不仅仅只有可读时才触发,以下情况也会触发:
channel 中数据读完
连接管道的另一端被关闭
有一个错误的 pending
对法发送消息过来
SelectionKey.OP_WRITE 一个等待写数据的通道可以说是”写就绪“
//通道注册读事件 即把Selector、SelectionKey 和channel建立关系
channel.register(this.selector, SelectionKey.OP_READ);
SelectionKey 对象的有效期间,Selector 会一直监控与 SelectionKey 对象相关的事件,如果事件发生,就会把 SelectionKey 对象加入到 selected-keys 集合中。
在以下情况下,SelectionKey 对象会失效,意味着 Selector 再也不会监控与它相关的事件:
1> 程序调用 SelectionKey 的cancel()方法
2> 关闭与 SelectionKey 关联的Channel
3> 与 SelectionKey 关联的 Selector 被关闭
Ready Set是通道已经准备就绪的操作的集合,在一个选择后,你会是首先访问这个Ready Set。
int readySet = selectionKey.readyOps();
// 轮询访问selector
while (true) {
// 当注册的事件到达时,方法返回;否则,该方法会一直阻塞
selector.select();
// 获得selector中选中的项的迭代器,选中的项为注册的事件
Iterator> ite = this.selector.selectedKeys().iterator();
while (ite.hasNext()) {
SelectionKey key = (SelectionKey) ite.next();
// 删除已选的key,以防重复处理
ite.remove();
// 客户端请求连接事件
if (key.isAcceptable()) {
handlerAccept(key);
// 获得了可读的事件
} else if (key.isReadable()) {
handelerRead(key);
}
}
}
通过SelectionKey对象获取其监听的Channel通道:Channel channel = selectionKey.channel();
通过SelectionKey对象获取其所属的选择器Selector:Selector selector = selectionKey.selector();
可以将一个对象或者更多的信息附着到SelectionKey上,这样就能方便的识别某个给定的通道。
例如,可以附加与通道一起使用的Buffer,或是包含聚集数据的某个对象。
selectionKey.attach(theObject);
Object attachedObj = selectionKey.attachment();
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; /** * NIO服务端 * * @author -琴兽- */ public class NIOServer { // 通道管理器 private Selector selector; //启动服务端测试 public static void main(String[] args) throws IOException { NIOServer server = new NIOServer(); server.initServer(8080); server.listen(); } //获得一个ServerSocket通道,并对该通道做一些初始化的工作 public void initServer(int port) throws IOException { // 获得一个ServerSocket通道 ServerSocketChannel serverChannel = ServerSocketChannel.open(); // 设置通道为非阻塞 serverChannel.configureBlocking(false); // 将该通道对应的ServerSocket绑定到port端口 serverChannel.socket().bind(new InetSocketAddress(port)); // 获得一个通道管理器 this.selector = Selector.open(); // 将通道管理器和该通道绑定,并为该通道注册SelectionKey.OP_ACCEPT事件,注册该事件后, // 当该事件到达时,selector.select()会返回,如果该事件没到达selector.select()会一直阻塞。 serverChannel.register(selector, SelectionKey.OP_ACCEPT); } /** * 采用轮询的方式监听selector上是否有需要处理的事件,如果有,则进行处理 */ public void listen() throws IOException { System.out.println("服务端启动成功!"); // 轮询访问selector while (true) { // 当注册的事件到达时,方法返回;否则,该方法会一直阻塞 selector.select(); // 获得selector中选中的项的迭代器,选中的项为注册的事件 Iterator> ite = this.selector.selectedKeys().iterator(); while (ite.hasNext()) { SelectionKey key = (SelectionKey) ite.next(); // 删除已选的key,以防重复处理 ite.remove(); handler(key); } } } /** * 处理请求 */ public void handler(SelectionKey key) throws IOException { // 客户端请求连接事件 if (key.isAcceptable()) { handlerAccept(key); // 获得了可读的事件 } else if (key.isReadable()) { handelerRead(key); } } /** * 处理连接请求 */ public void handlerAccept(SelectionKey key) throws IOException { ServerSocketChannel server = (ServerSocketChannel) key.channel(); // 获得和客户端连接的通道 SocketChannel channel = server.accept(); // 设置成非阻塞 channel.configureBlocking(false); // 在这里可以给客户端发送信息哦 System.out.println("新的客户端连接"); // 在和客户端连接成功之后,为了可以接收到客户端的信息,需要给通道设置读的权限。 channel.register(this.selector, SelectionKey.OP_READ); } /** * 处理读的事件 * * @param key * @throws IOException */ public void handelerRead(SelectionKey key) throws IOException { // 服务器可读取消息:得到事件发生的Socket通道 SocketChannel channel = (SocketChannel) key.channel(); // 创建读取的缓冲区 ByteBuffer buffer = ByteBuffer.allocate(1024); int read = channel.read(buffer); if(read > 0){ byte[] data = buffer.array(); String msg = new String(data).trim(); System.out.println("服务端收到信息:" + msg); //回写数据 ByteBuffer outBuffer = ByteBuffer.wrap("好的".getBytes()); channel.write(outBuffer);// 将消息回送给客户端 }else{ System.out.println("客户端关闭"); key.cancel(); } } }