SelectionKey 和 IO 事件紧密相关。
一个SelectionKey表示一个Channel对象和一个selector之间注册绑定的关系。
通常意义上来讲,一个SelectionKey对象代表着一个channel和它注册的selector之间的关系。
SelectionKey#channel() : 返回与该键相关的SelectableChannel对象。
SelectionKey#selector() : 返回相关的selector对象。
此外,SelectionKey还有两个比较重要的属性,两个以整数形式进行编码的比特掩码:
interestOps
代表channel所感兴趣的事件的集合。interest集合是使用注册通道时给定的值初始化的,可以通过调用键对象的interestOps(int ops)方法进行修改。
同时可以调用键对象的interestOps()方法获取所有感兴趣的事件。当相关的selector上的select操作正在进行时改变键的interest集合,不会影响那个正在进行的select的操作,
所有更改将会在下一个select中作用出来。
readyOps
代表,interest集合在上次调用了select之后,所有准备就绪的事件。他是interestOps的自己,注册通道时,初始值为0,只有在选择器选择期间可能被更新。
可以调用键对象的readyOps()方法获取所有准备就绪的事件。需要注意的是,ready集合返回的就绪状态仅仅是一个提示,不是保证。底层的通道在任何时候都可以改变,
其他的线程可能在通道上执行,从而影响它的就绪状态。
AbstractSelectionKey是一个抽象类,相对功能比较简单,就是判断当前key是否有效。
public abstract class AbstractSelectionKey
extends SelectionKey
{
/**
* Initializes a new instance of this class.
*/
protected AbstractSelectionKey() { }
private volatile boolean valid = true;
public final boolean isValid() {
return valid;
}
void invalidate() { // package-private
valid = false;
}
// 注意这个方法的含义。
/**
* Cancels this key.
*
* <p> If this key has not yet been cancelled then it is added to its
* selector's cancelled-key set while synchronized on that set. </p>
*/
public final void cancel() {
// Synchronizing "this" to prevent this key from getting canceled
// multiple times by different threads, which might cause race
// condition between selector's select() and channel's close().
synchronized (this) {
if (valid) {
valid = false;
((AbstractSelector)selector()).cancel(this);
}
}
}
}
serverChannel.register(selctor, SelectionKey:key)
也可以分开注册:
sk = serverChannel.register(selctor, 0)
sk.interestOps(SelectionKey:key)
然后通过SelectionKey实例就可以获取内部的selector和channel了:
public void run(){
server.register(selector, SelectionKey.OP_ACCEPT);
SelectionKey sk = server.register(selector, 0);
sk.interestOps(SelectionKey.OP_ACCEPT);
SelectableChannel channel = sk.channel();
Selector selector = sk.selector();
}
前面提到了SelectionKey的两个重要属性 interestOps 和 readyOps ,它的具体实现是在 SelectionKeyImpl
中 ,以下是源码:
public class SelectionKeyImpl extends AbstractSelectionKey {
final SelChImpl channel;
public final SelectorImpl selector;
private int index;
private volatile int interestOps;
private int readyOps;
...
}
SelectionKey可以通过interestOps()方法返回所感兴趣的事件,然后再和不同事件的值进行与运算计算所对应的类型是否就绪。
SelectionKey通过readyOps获取就绪事件集合,
sk.isAcceptable();
sk.isConnectable();
sk.isReadable();
sk.isWritable();
public final boolean isAcceptable() {
return (readyOps() & OP_ACCEPT) != 0;
}
Set<SelectionKey> selectionKeys = selector.selectedKeys();
Iterator<SelectionKey> iter = selectionKeys.iterator();
while (iter.hasNext()) {
SelectionKey key = iter.next();
if (key.isAcceptable()) {
acceptHandle(key);
} else if (key.isReadable()) {
readHandle(key);
}
}
首先循环判断selectionKey中是否有就绪事件,如果有就绪事件,则判断就绪是是什么类型的事件,然后进行处理。
public void acceptHandle(SelectionKey key) throws IOException {
ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
SocketChannel client = ssc.accept();
client.configureBlocking(false);
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(1024);
client.register(selector, SelectionKey.OP_READ, byteBuffer);
System.err.println("client arrived " + client.getRemoteAddress());
}
public void readHandle(SelectionKey key) throws IOException {
SocketChannel client = (SocketChannel) key.channel();
ByteBuffer buffer = (ByteBuffer) key.attachment();
buffer.clear();
int read = 0;
while (true) {
read = client.read(buffer);
if (read > 0) {
// 服务端读到的数据,再写一遍给到客户端
buffer.flip();
while (buffer.hasRemaining()) {
client.write(buffer);
}
buffer.clear();
} else if (read == 0) {
break;
} else {
// client 发生错误 或者断开 read == -1
// 导致空转 最终CPU达到100%
client.close();
break;
}
}
}