Java NIO Selector詳解

Selector selector = Selector.open();
將 Channel 注冊(cè)到選擇器中
channel.configureBlocking(false);
SelectionKey?key?=?channel.register(selector,?SelectionKey.OP_READ);
注意,如果一個(gè) Channel 要注冊(cè)到 Selector 中,那么這個(gè) Channel 必須是非阻塞的,即channel.configureBlocking(false);
因?yàn)?Channel 必須要是非阻塞的,因此 FileChannel 是不能夠使用選擇器的,因?yàn)?FileChannel 都是阻塞的.
Connect,即連接事件(TCP 連接),?對(duì)應(yīng)于SelectionKey.OP_CONNECT。
Accept,即確認(rèn)事件,對(duì)應(yīng)于SelectionKey.OP_ACCEPT。
Read,即讀事件,對(duì)應(yīng)于SelectionKey.OP_READ, 表示 buffer 可讀。
Write,即寫(xiě)事件,對(duì)應(yīng)于SelectionKey.OP_WRITE, 表示 buffer 可寫(xiě)。
一個(gè) Channel發(fā)出一個(gè)事件也可以稱(chēng)為對(duì)于某個(gè)事件, Channel 準(zhǔn)備好了。因此一個(gè) Channel 成功連接到了另一個(gè)服務(wù)器也可以被稱(chēng)為 connect ready。
int interestSet = SelectionKey.OP_READ | SelectionKey.OP_WRITE;
channel.register(selector, SelectionKey.OP_READ);
channel.register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE);
int interestSet = selectionKey.interestOps();
boolean isInterestedInAccept = interestSet & SelectionKey.OP_ACCEPT;
boolean isInterestedInConnect = interestSet & SelectionKey.OP_CONNECT;
boolean isInterestedInRead = interestSet & SelectionKey.OP_READ;
boolean isInterestedInWrite = interestSet & SelectionKey.OP_WRITE;
int?readySet?=?selectionKey.readyOps();
selectionKey.isAcceptable();
selectionKey.isConnectable();
selectionKey.isReadable();
selectionKey.isWritable();
Channel 和 Selector
Channel channel = selectionKey.channel();
Selector selector = selectionKey.selector();
Attaching Object
selectionKey.attach(theObject);
Object attachedObj = selectionKey.attachment();
SelectionKey key = channel.register(selector, SelectionKey.OP_READ, theObject);
通過(guò) Selector 選擇 Channel
注意:select()方法返回的值表示有多少個(gè) Channel 可操作.
獲取可操作的 Channel
Set
selectedKeys = selector.selectedKeys();
Iterator
keyIterator = selectedKeys.iterator();
while(keyIterator.hasNext()) {
SelectionKey key = keyIterator.next();
if(key.isAcceptable()) {
// a connection was accepted by a ServerSocketChannel.
} else if (key.isConnectable()) {
// a connection was established with a remote server.
} else if (key.isReadable()) {
// a channel is ready for reading
} else if (key.isWritable()) {
// a channel is ready for writing
}
keyIterator.remove();
}
-
通過(guò) Selector.open() 打開(kāi)一個(gè) Selector. -
將 Channel 注冊(cè)到 Selector 中, 并設(shè)置需要監(jiān)聽(tīng)的事件(interest set) -
不斷重復(fù): *從 selected key 中獲取 對(duì)應(yīng)的 Channel 和附加信息(如果有的話(huà))
關(guān)閉 Selector
完整的 Selector 例子
public class NioEchoServer {
private static final int BUF_SIZE = 256;
private static final int TIMEOUT = 3000;
public static void main(String args[]) throws Exception {
// 打開(kāi)服務(wù)端 Socket
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
// 打開(kāi) Selector
Selector selector = Selector.open();
// 服務(wù)端 Socket 監(jiān)聽(tīng)8080端口, 并配置為非阻塞模式
serverSocketChannel.socket().bind(new InetSocketAddress(8080));
serverSocketChannel.configureBlocking(false);
// 將 channel 注冊(cè)到 selector 中.
// 通常我們都是先注冊(cè)一個(gè) OP_ACCEPT 事件, 然后在 OP_ACCEPT 到來(lái)時(shí), 再將這個(gè) Channel 的 OP_READ
// 注冊(cè)到 Selector 中.
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
while (true) {
// 通過(guò)調(diào)用 select 方法, 阻塞地等待 channel I/O 可操作
if (selector.select(TIMEOUT) == 0) {
System.out.print(".");
continue;
}
// 獲取 I/O 操作就緒的 SelectionKey, 通過(guò) SelectionKey 可以知道哪些 Channel 的哪類(lèi) I/O 操作已經(jīng)就緒.
Iterator
keyIterator = selector.selectedKeys().iterator();
while (keyIterator.hasNext()) {
SelectionKey key = keyIterator.next();
// 當(dāng)獲取一個(gè) SelectionKey 后, 就要將它刪除, 表示我們已經(jīng)對(duì)這個(gè) IO 事件進(jìn)行了處理.
keyIterator.remove();
if (key.isAcceptable()) {
// 當(dāng) OP_ACCEPT 事件到來(lái)時(shí), 我們就有從 ServerSocketChannel 中獲取一個(gè) SocketChannel,
// 代表客戶(hù)端的連接
// 注意, 在 OP_ACCEPT 事件中, 從 key.channel() 返回的 Channel 是 ServerSocketChannel.
// 而在 OP_WRITE 和 OP_READ 中, 從 key.channel() 返回的是 SocketChannel.
SocketChannel clientChannel = ((ServerSocketChannel) key.channel()).accept();
clientChannel.configureBlocking(false);
//在 OP_ACCEPT 到來(lái)時(shí), 再將這個(gè) Channel 的 OP_READ 注冊(cè)到 Selector 中.
// 注意, 這里我們?nèi)绻麤](méi)有設(shè)置 OP_READ 的話(huà), 即 interest set 仍然是 OP_CONNECT 的話(huà), 那么 select 方法會(huì)一直直接返回.
clientChannel.register(key.selector(), OP_READ, ByteBuffer.allocate(BUF_SIZE));
}
if (key.isReadable()) {
SocketChannel clientChannel = (SocketChannel) key.channel();
ByteBuffer buf = (ByteBuffer) key.attachment();
long bytesRead = clientChannel.read(buf);
if (bytesRead == -1) {
clientChannel.close();
} else if (bytesRead > 0) {
key.interestOps(OP_READ | SelectionKey.OP_WRITE);
System.out.println("Get data length: " + bytesRead);
}
}
if (key.isValid() && key.isWritable()) {
ByteBuffer buf = (ByteBuffer) key.attachment();
buf.flip();
SocketChannel clientChannel = (SocketChannel) key.channel();
clientChannel.write(buf);
if (!buf.hasRemaining()) {
key.interestOps(OP_READ);
}
buf.compact();
}
}
}
}
}
轉(zhuǎn)載源:SegmentFault
特別推薦一個(gè)分享架構(gòu)+算法的優(yōu)質(zhì)內(nèi)容,還沒(méi)關(guān)注的小伙伴,可以長(zhǎng)按關(guān)注一下:
長(zhǎng)按訂閱更多精彩▼
如有收獲,點(diǎn)個(gè)在看,誠(chéng)摯感謝
免責(zé)聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺(tái)僅提供信息存儲(chǔ)服務(wù)。文章僅代表作者個(gè)人觀點(diǎn),不代表本平臺(tái)立場(chǎng),如有問(wèn)題,請(qǐng)聯(lián)系我們,謝謝!